WorkflowDomainService.cs 196 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034
  1. using Hotline.Caching.Interfaces;
  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 Microsoft.Extensions.Logging;
  15. using SqlSugar;
  16. using Hotline.EventBus;
  17. using XF.Domain.Authentications;
  18. using XF.Domain.Dependency;
  19. using XF.Domain.Entities;
  20. using XF.Domain.Exceptions;
  21. using XF.Domain.Repository;
  22. using System.Text;
  23. using Hotline.Configurations;
  24. using Hotline.Share.Dtos.File;
  25. using Hotline.Share.Dtos.FlowEngine.Workflow;
  26. using Microsoft.Extensions.Options;
  27. namespace Hotline.FlowEngine.Workflows
  28. {
  29. public class WorkflowDomainService : IWorkflowDomainService, IScopeDependency
  30. {
  31. private readonly IWorkflowRepository _workflowRepository;
  32. private readonly IRepository<WorkflowStep> _workflowStepRepository;
  33. private readonly IRepository<WorkflowTrace> _workflowTraceRepository;
  34. private readonly IRepository<WorkflowCountersign> _workflowCountersignRepository;
  35. private readonly IMapper _mapper;
  36. private readonly Publisher _publisher;
  37. private readonly ILogger<WorkflowDomainService> _logger;
  38. private readonly IFileRepository _fileRepository;
  39. private readonly IRepository<User> _userRepository;
  40. private readonly ISystemSettingCacheManager _systemSettingCacheManager;
  41. private readonly IWfModuleCacheManager _wfModuleCacheManager;
  42. private readonly ISessionContextProvider _sessionContextProvider;
  43. private readonly IOptionsSnapshot<AppConfiguration> _appOptions;
  44. public WorkflowDomainService(
  45. IWorkflowRepository workflowRepository,
  46. IRepository<WorkflowStep> workflowStepRepository,
  47. IRepository<WorkflowTrace> workflowTraceRepository,
  48. IRepository<WorkflowCountersign> workflowCountersignRepository,
  49. ISystemSettingCacheManager systemSettingCacheManager,
  50. IWfModuleCacheManager wfModuleCacheManager,
  51. ISessionContextProvider sessionContextProvider,
  52. IMapper mapper,
  53. Publisher publisher,
  54. ILogger<WorkflowDomainService> logger,
  55. IOptionsSnapshot<AppConfiguration> appOptions,
  56. IFileRepository fileRepository)
  57. {
  58. _workflowRepository = workflowRepository;
  59. _workflowStepRepository = workflowStepRepository;
  60. _workflowTraceRepository = workflowTraceRepository;
  61. _workflowCountersignRepository = workflowCountersignRepository;
  62. _mapper = mapper;
  63. _publisher = publisher;
  64. _logger = logger;
  65. _fileRepository = fileRepository;
  66. _systemSettingCacheManager = systemSettingCacheManager;
  67. _wfModuleCacheManager = wfModuleCacheManager;
  68. _sessionContextProvider = sessionContextProvider;
  69. _appOptions = appOptions;
  70. }
  71. public async Task<Workflow> CreateWorkflowAsync(WorkflowModule wfModule, string title, string userId,
  72. string orgId, string? externalId = null, CancellationToken cancellationToken = default)
  73. {
  74. var definition = wfModule.Definition;
  75. if (definition is null)
  76. throw new UserFriendlyException("无效流程模板");
  77. var workflow = new Workflow
  78. {
  79. Title = title,
  80. ModuleId = wfModule.Id,
  81. ModuleName = wfModule.Name,
  82. ModuleCode = wfModule.Code,
  83. DefinitionId = definition.Id,
  84. Status = EWorkflowStatus.Runnable,
  85. Steps = new(),
  86. Traces = new(),
  87. WorkflowDefinition = definition,
  88. ExternalId = externalId ?? string.Empty,
  89. // //FlowedOrgIds = new List<string> { orgId },
  90. // FlowedUserIds = new List<string> { userId },
  91. FlowType = definition.FlowType,
  92. };
  93. await _workflowRepository.AddAsync(workflow, cancellationToken);
  94. return workflow;
  95. }
  96. /// <summary>
  97. /// 流程开始
  98. /// </summary>
  99. public async Task StartAsync(Workflow workflow, WorkflowStep startStep, BasicWorkflowDto dto,
  100. StepDefine firstStepDefine, bool isNextDynamic, FlowAssignInfo flowAssignInfo,
  101. ECounterSignType? counterSignType, DateTime? expiredTime,
  102. CancellationToken cancellationToken)
  103. {
  104. if (firstStepDefine.StepType is EStepType.End)
  105. {
  106. await _publisher.PublishAsync(
  107. new StartWorkflowNotify(workflow, dto, flowAssignInfo.FlowAssignType, startStep.WorkflowTrace),
  108. PublishStrategy.ParallelWhenAll, cancellationToken);
  109. //firstStep是否为end,t: 实际办理节点为startStep, 并且handlerId赋值 f: 实际办理节点为firstStep, handlerId未赋值
  110. workflow.UpdateActualStepWhenHandle(startStep, _sessionContextProvider.SessionContext.OrgAreaCode,
  111. _sessionContextProvider.SessionContext.OrgAreaName, _sessionContextProvider.SessionContext.OrgLevel);
  112. workflow.UpdateCurrentStepWhenHandle(startStep, _sessionContextProvider.SessionContext.OrgAreaCode,
  113. _sessionContextProvider.SessionContext.OrgAreaName, _sessionContextProvider.SessionContext.OrgLevel);
  114. var endTrace = await EndAsync(_sessionContextProvider.SessionContext, workflow, dto, firstStepDefine, startStep, expiredTime,
  115. cancellationToken);
  116. return;
  117. }
  118. var assigner = new UserInfo(
  119. _sessionContextProvider.SessionContext.UserId,
  120. _sessionContextProvider.SessionContext.UserName,
  121. _sessionContextProvider.SessionContext.OrgId,
  122. _sessionContextProvider.SessionContext.OrgName,
  123. _sessionContextProvider.SessionContext.OrgIsCenter
  124. );
  125. //firststeps
  126. var firstSteps = await CreateNextStepsAsync(workflow, startStep, dto, firstStepDefine, assigner,
  127. isNextDynamic, expiredTime, dto.IsStartCountersign, cancellationToken: cancellationToken);
  128. await _workflowStepRepository.UpdateAsync(startStep, cancellationToken);
  129. //handle trace
  130. var trace = await NextTraceAsync(workflow, dto, startStep, cancellationToken);
  131. //指派实际办理节点
  132. UpdateActualStep(workflow, dto, firstStepDefine, firstSteps);
  133. //更新实际办理节点
  134. UpdateCurrentStep(workflow, dto, firstStepDefine, firstSteps);
  135. //发起会签时记录顶层会签节点(必须在update currentStep之后)
  136. if (dto.IsStartCountersign && !workflow.IsInCountersign)
  137. workflow.StartCountersign(startStep.Id, counterSignType);
  138. await _workflowRepository.UpdateAsync(workflow, cancellationToken);
  139. //publish
  140. await _publisher.PublishAsync(new StartWorkflowNotify(workflow, dto, flowAssignInfo.FlowAssignType, trace),
  141. PublishStrategy.ParallelWhenAll, cancellationToken);
  142. }
  143. /// <summary>
  144. /// new(开启流程并停留在开始节点,开始节点为待办节点,指派给当前操作人)
  145. /// </summary>
  146. public async Task<(Workflow, WorkflowStep)> StartAsync(StartWorkflowDto dto,
  147. string externalId, DateTime? expiredTime, CancellationToken cancellationToken = default)
  148. {
  149. // var validator = new StartWorkflowDtoValidator();
  150. // var validResult = await validator.ValidateAsync(dto, cancellationToken);
  151. // if (!validResult.IsValid)
  152. // throw new UserFriendlyException(
  153. // $"非法参数, {string.Join(',', validResult.Errors.Select(d => d.ErrorMessage))}");
  154. var wfModule = await GetWorkflowModuleAsync(dto.DefinitionModuleCode, cancellationToken);
  155. var definition = wfModule.Definition;
  156. if (definition == null)
  157. throw new UserFriendlyException("无效模板编码");
  158. if (definition.Status is not EDefinitionStatus.Enable)
  159. throw new UserFriendlyException("该模板不可用");
  160. //如果发起会签需检查是否支持发起会签
  161. var startStepDefine = definition.FindStartStepDefine();
  162. //下一节点是否为动态节点
  163. var isNextDynamic = startStepDefine.InstanceMode is EInstanceMode.Dynamic &&
  164. !DynamicShouldTerminal(startStepDefine, _sessionContextProvider.SessionContext.OrgLevel);
  165. var firstStepDefine = isNextDynamic
  166. ? startStepDefine
  167. : definition.FindStepDefine(dto.NextStepCode);
  168. if (firstStepDefine is null)
  169. throw new UserFriendlyException("未查询到下一步节点配置");
  170. //1. 如果不是按角色指派,handlers必填 2. 如果按角色指派,handlers可以不选
  171. if (firstStepDefine.HandlerType is not EHandlerType.Role && !dto.NextHandlers.Any())
  172. throw UserFriendlyException.SameMessage("未指派办理人");
  173. if (dto.IsStartCountersign)
  174. {
  175. if (!startStepDefine.CanStartCountersign)
  176. throw new UserFriendlyException("当前节点不支持发起会签");
  177. //if (startStepDefine.HandlerType is EHandlerType.Role)
  178. // throw new UserFriendlyException("当前节点不支持发起会签");
  179. //即使当前节点支持发起会签,但下一节点为信息汇总节点、结束节点时也不可发起会签
  180. if (firstStepDefine.StepType is EStepType.Summary or EStepType.End)
  181. throw new UserFriendlyException("下一节点不允许发起会签");
  182. //下一节点是会签汇总节点也不允许发起会签
  183. if (dto.BackToCountersignEnd)
  184. throw new UserFriendlyException("下一节点不允许发起会签");
  185. }
  186. var workflow = CreateWorkflow(wfModule, dto.Title, externalId);
  187. var defineHandler = startStepDefine.HandlerTypeItems.First();
  188. var assigner = new UserInfo(
  189. _sessionContextProvider.SessionContext.UserId,
  190. _sessionContextProvider.SessionContext.UserName,
  191. _sessionContextProvider.SessionContext.OrgId,
  192. _sessionContextProvider.SessionContext.OrgName,
  193. _sessionContextProvider.SessionContext.OrgIsCenter
  194. );
  195. var handler = new FlowStepHandler
  196. {
  197. Key = _sessionContextProvider.SessionContext.RequiredUserId,
  198. Value = _sessionContextProvider.SessionContext.UserName,
  199. UserId = _sessionContextProvider.SessionContext.UserId,
  200. Username = _sessionContextProvider.SessionContext.UserName,
  201. OrgId = _sessionContextProvider.SessionContext.RequiredOrgId,
  202. OrgName = _sessionContextProvider.SessionContext.OrgName,
  203. RoleId = defineHandler.Key,
  204. RoleName = defineHandler.Value,
  205. };
  206. var startStep = CreateStartStep(workflow, startStepDefine, dto, assigner, handler, expiredTime);
  207. if (dto.Files.Any())
  208. startStep.FileJson =
  209. await _fileRepository.AddFileAsync(dto.Files, workflow.ExternalId, startStep.Id, cancellationToken);
  210. await _workflowStepRepository.AddAsync(startStep, cancellationToken);
  211. workflow.Steps.Add(startStep);
  212. //starttrace
  213. var startTrace = _mapper.Map<WorkflowTrace>(startStep);
  214. startTrace.StepId = startStep.Id;
  215. startTrace.TraceType = EWorkflowTraceType.Normal;
  216. await _workflowTraceRepository.AddAsync(startTrace, cancellationToken);
  217. workflow.Traces.Add(startTrace);
  218. startStep.WorkflowTrace = startTrace;
  219. //更新受理人信息
  220. workflow.UpdateAcceptor(
  221. _sessionContextProvider.SessionContext.RequiredUserId,
  222. _sessionContextProvider.SessionContext.UserName,
  223. _sessionContextProvider.SessionContext.StaffNo,
  224. _sessionContextProvider.SessionContext.RequiredOrgId,
  225. _sessionContextProvider.SessionContext.OrgName);
  226. workflow.UpdateActualStepWhenAssign(startStep, handler);
  227. workflow.UpdateCurrentStepWhenAssign(startStep, handler);
  228. await _workflowRepository.AddAsync(workflow, cancellationToken);
  229. var flowAssignInfo =
  230. await GetNextStepFlowAssignInfoAsync(workflow, startStep, dto, firstStepDefine, isNextDynamic, cancellationToken);
  231. //publish
  232. await _publisher.PublishAsync(new StartWorkflowNotify(workflow, dto, flowAssignInfo.FlowAssignType, startTrace),
  233. PublishStrategy.ParallelWhenAll, cancellationToken);
  234. return (workflow, startStep);
  235. }
  236. /// <summary>
  237. /// new
  238. /// </summary>
  239. public async Task<List<WorkflowStep>> NextAsync(ISessionContext current, NextWorkflowDto dto,
  240. DateTime? expiredTime = null, bool isAutoFillSummaryOpinion = false,
  241. Action<WorkflowStep>? stepConfig = null, CancellationToken cancellationToken = default)
  242. {
  243. var workflow = await GetWorkflowAsync(dto.WorkflowId, withDefine: true, withSteps: true,
  244. withTraces: true, withCountersigns: true, cancellationToken: cancellationToken);
  245. CheckWhetherRunnable(workflow.Status);
  246. var currentStep = workflow.Steps.FirstOrDefault(d => d.Id == dto.StepId);
  247. if (currentStep == null)
  248. throw new UserFriendlyException(
  249. $"未找到对应节点, workflowId: {dto.WorkflowId}, stepId: {dto.StepId}", "未找到对应节点");
  250. if (currentStep.Status is EWorkflowStepStatus.Handled)
  251. throw new UserFriendlyException("该状态不支持继续办理");
  252. //todo 校验currentStep 能否由 current 办理
  253. var currentStepDefine = GetStepDefine(workflow.WorkflowDefinition, currentStep.Code);
  254. //下一节点是否为动态节点
  255. var isNextDynamic = (string.IsNullOrEmpty(dto.NextStepCode) || dto.NextStepCode.ToLower() != "end")
  256. && currentStepDefine.InstanceMode is EInstanceMode.Dynamic
  257. && !DynamicShouldTerminal(currentStepDefine, current.OrgLevel);
  258. StepDefine nextStepDefine;
  259. if (isNextDynamic
  260. || (workflow.IsInCountersign
  261. && currentStep.IsInCountersign()
  262. && !currentStep.IsTopCountersignEndStep(workflow.TopCountersignStepId))
  263. || dto.IsStartCountersign)
  264. {
  265. //下一步配置为当前节点配置
  266. nextStepDefine = currentStepDefine;
  267. }
  268. else
  269. {
  270. //下一步配置为下一步节点配置
  271. nextStepDefine = GetStepDefine(workflow.WorkflowDefinition, dto.NextStepCode);
  272. }
  273. //需求:按角色选择办理人可以不选,表示该角色下所有人都可以办理,同时依据配置:是否本部门人办理显示待选办理人。角色下只要一人办理即可(即:角色下不发起会签)
  274. if (nextStepDefine.HandlerType != EHandlerType.Role
  275. && nextStepDefine.StepType != EStepType.End
  276. && !dto.NextHandlers.Any())
  277. throw new UserFriendlyException("未指定节点处理者");
  278. if (dto.IsStartCountersign)
  279. {
  280. if (!currentStepDefine.CanStartCountersign)
  281. throw UserFriendlyException.SameMessage("当前节点不支持发起会签");
  282. //即使当前节点支持发起会签,但下一节点为信息汇总节点、结束节点时也不可发起会签
  283. if (nextStepDefine.StepType is EStepType.Summary or EStepType.End)
  284. throw UserFriendlyException.SameMessage("下一汇总节点不允许发起会签");
  285. //下一节点是会签汇总节点也不允许发起会签
  286. if (dto.BackToCountersignEnd)
  287. throw UserFriendlyException.SameMessage("下一会签汇总节点不允许发起会签");
  288. }
  289. // var flowAssignInfo =
  290. // await GetNextStepFlowAssignInfoAsync(workflow, currentStep, dto, nextStepDefine, isNextDynamic, cancellationToken);
  291. dto.FlowAssignType ??= SetNextStepAssignInfo(workflow, currentStep, dto, nextStepDefine, isNextDynamic);
  292. /*
  293. 0.动态、会签固定指派方式
  294. 1.普通办理方式:依据配置决定指派对象,结合dto选择的nextHandlers
  295. 2.下一节点是按角色办理时可以不选办理对象,此时需指派给配置的角色
  296. 3.下一节点是汇总节点切未选办理对象时:a.依据原节点办理人办理 b.依据原节点配置办理
  297. 4.开放action,允许按业务逻辑任意指派
  298. 5.任意场景下优先级:action > dto选择办理对象 > 业务逻辑指定
  299. */
  300. //普通节点往汇总节点办理时可以不选,不选的场景主动去查之前的办理对象 //todo 按照指派策略生成办理对象
  301. if (nextStepDefine.StepType is EStepType.Summary && !dto.NextHandlers.Any())
  302. {
  303. var handler = GetSummaryTargetFlowStepHandler(workflow, nextStepDefine.SummaryTargetCode);
  304. dto.NextHandlers.Add(handler);
  305. }
  306. //todo 加入到重构获取办理对象方法中
  307. if (nextStepDefine.StepType != EStepType.End
  308. && nextStepDefine.HandlerType == EHandlerType.Role
  309. && !dto.NextHandlers.Any())
  310. {
  311. //todo 指派给配置的角色
  312. dto.FlowAssignType = EFlowAssignType.Role;
  313. var handler = nextStepDefine.HandlerTypeItems.First();
  314. dto.NextHandlers.Add(new FlowStepHandler
  315. {
  316. Key = handler.Key,
  317. Value = handler.Value,
  318. RoleId = handler.Key,
  319. RoleName = handler.Value
  320. });
  321. }
  322. #region 办理当前节点
  323. if (dto.Files != null && dto.Files.Any())
  324. currentStep.FileJson = await _fileRepository.AddFileAsync(
  325. dto.Files, workflow.ExternalId, currentStep.Id, cancellationToken);
  326. var counterSignType = GetCounterSignType(dto.IsStartCountersign, currentStep.BusinessType);
  327. var updateSteps = new List<WorkflowStep> { currentStep };
  328. //结束当前会签流程
  329. if (currentStep.IsCountersignEndStep)
  330. {
  331. var countersignStartStep = workflow.Steps.FirstOrDefault(d => d.Id == currentStep.CountersignStartStepId);
  332. if (countersignStartStep is null)
  333. throw new UserFriendlyException(
  334. $"未查询到会签开始step, workflowId: {workflow.Id}, currentStepId: {currentStep.Id}",
  335. "未查询到会签开始节点");
  336. if (countersignStartStep.IsStartCountersign)
  337. {
  338. var currentCountersign =
  339. workflow.Countersigns.FirstOrDefault(d => d.Id == countersignStartStep.StartCountersignId);
  340. if (currentCountersign is null)
  341. throw new UserFriendlyException(
  342. $"未查询到对应会签信息,workflowId:{workflow.Id}, countersignId:{currentStep.CountersignId}",
  343. "无效会签编号");
  344. //结束step会签信息
  345. countersignStartStep.CountersignEnd();
  346. updateSteps.Add(countersignStartStep);
  347. //结束会签
  348. currentCountersign.End(currentStep.Id, currentStep.Code, currentStep.BusinessType,
  349. current.UserId, current.UserName,
  350. current.OrgId, current.OrgName,
  351. current.OrgAreaCode, current.OrgAreaName);
  352. await _workflowCountersignRepository.UpdateAsync(currentCountersign, cancellationToken);
  353. }
  354. }
  355. await HandleStepAsync(currentStep, workflow, dto, counterSignType, expiredTime, EHandleMode.Normal, cancellationToken);
  356. currentStep.IsActualHandled = CheckIsActualHandle(workflow, currentStep, nextStepDefine, dto);
  357. _mapper.Map(dto, workflow);
  358. //会签办理节点办理时更新会签members字段
  359. if (currentStep.CountersignPosition is ECountersignPosition.Direct or ECountersignPosition.Indirect)
  360. {
  361. if (!string.IsNullOrEmpty(currentStep.CountersignId))
  362. {
  363. //会签中正常办理节点,更新会签members办理状态
  364. var countersign = workflow.Countersigns.FirstOrDefault(d =>
  365. !d.IsCompleted() && d.Id == currentStep.CountersignId);
  366. if (countersign is not null)
  367. {
  368. //throw new UserFriendlyException(
  369. // $"会签数据异常, workflowId: {currentStep.WorkflowId}, countersignId: {currentStep.CountersignId}",
  370. // "会签数据异常");
  371. countersign.MemberHandled(current.UserId, current.OrgId);
  372. //update cs
  373. await _workflowCountersignRepository.UpdateNav(countersign)
  374. .Include(d => d.Members)
  375. .ExecuteCommandAsync();
  376. }
  377. }
  378. }
  379. await _workflowStepRepository.UpdateRangeAsync(updateSteps, cancellationToken);
  380. //更新traces
  381. var updateTraces = new List<WorkflowTrace>();
  382. foreach (var updateStep in updateSteps)
  383. {
  384. var updateTrace = workflow.Traces.First(d => d.Id == updateStep.Id);
  385. _mapper.Map(updateStep, updateTrace);
  386. updateTraces.Add(updateTrace);
  387. }
  388. await _workflowTraceRepository.UpdateRangeAsync(updateTraces, cancellationToken);
  389. if (workflow.ActualHandleStepId == currentStep.Id)
  390. {
  391. //更新实际办理节点信息
  392. workflow.UpdateActualStepWhenHandle(currentStep, _sessionContextProvider.SessionContext.OrgAreaCode,
  393. _sessionContextProvider.SessionContext.OrgAreaName, _sessionContextProvider.SessionContext.OrgLevel);
  394. }
  395. if (workflow.CurrentStepId == currentStep.Id)
  396. {
  397. workflow.UpdateCurrentStepWhenHandle(currentStep, _sessionContextProvider.SessionContext.OrgAreaCode,
  398. _sessionContextProvider.SessionContext.OrgAreaName, _sessionContextProvider.SessionContext.OrgLevel);
  399. }
  400. //var trace = await NextTraceAsync(workflow, dto, currentStep, cancellationToken);
  401. #endregion
  402. #region 处理流程
  403. //检查会签是否结束,并更新当前会签节点字段
  404. var isCountersignOver = false;
  405. if (workflow.IsInCountersign && currentStep.IsCountersignEndStep)
  406. {
  407. isCountersignOver = workflow.CheckIfCountersignOver();
  408. if (isCountersignOver)
  409. workflow.EndCountersign();
  410. }
  411. //检查是否流转到流程终点
  412. if (nextStepDefine.StepType is EStepType.End)
  413. {
  414. if (string.IsNullOrEmpty(workflow.ActualHandlerId)
  415. || string.IsNullOrEmpty(workflow.ActualHandleOrgCode)) //开始流程直接归档
  416. {
  417. //更新实际办理节点信息
  418. workflow.UpdateActualStepWhenHandle(currentStep, _sessionContextProvider.SessionContext.OrgAreaCode,
  419. _sessionContextProvider.SessionContext.OrgAreaName, _sessionContextProvider.SessionContext.OrgLevel);
  420. workflow.ActualHandleStepAcceptTime = currentStep.AcceptTime;
  421. workflow.ActualHandleTime = currentStep.HandleTime;
  422. }
  423. var endTrace = await EndAsync(current, workflow, dto, nextStepDefine, currentStep, expiredTime, cancellationToken);
  424. return new List<WorkflowStep>();
  425. }
  426. var isStartCountersign = currentStep.CountersignPosition switch
  427. {
  428. ECountersignPosition.None => dto.IsStartCountersign,
  429. ECountersignPosition.Direct => !dto.BackToCountersignEnd,
  430. ECountersignPosition.Indirect => !dto.BackToCountersignEnd,
  431. ECountersignPosition.End => !dto.BackToCountersignEnd && (workflow.IsInCountersign || dto.IsStartCountersign),
  432. _ => throw new ArgumentOutOfRangeException()
  433. };
  434. var currentTrace = workflow.Traces.First(d => d.Id == currentStep.Id);
  435. //创建会签数据
  436. if (isStartCountersign)
  437. {
  438. var exists = workflow.Countersigns.Any(d =>
  439. !d.IsCompleted() && !string.IsNullOrEmpty(current.UserId) && d.StarterId == current.UserId);
  440. if (exists)
  441. throw new UserFriendlyException($"该用户在当前流程存在未结束会签, workflowId: {workflow.Id}, userId: {current.UserId}");
  442. var countersign = await StartCountersignAsync(current, workflow, currentStep, dto,
  443. counterSignType, expiredTime, cancellationToken);
  444. currentTrace.StartCountersign(countersign.Id);
  445. await _workflowStepRepository.UpdateAsync(currentStep, cancellationToken);
  446. await _workflowTraceRepository.UpdateAsync(currentTrace, cancellationToken);
  447. }
  448. //发起会签时记录顶层会签节点
  449. if (dto.IsStartCountersign && !workflow.IsInCountersign)
  450. workflow.StartCountersign(currentStep.Id, counterSignType);
  451. var assigner = new UserInfo(
  452. _sessionContextProvider.SessionContext.UserId,
  453. _sessionContextProvider.SessionContext.UserName,
  454. _sessionContextProvider.SessionContext.OrgId,
  455. _sessionContextProvider.SessionContext.OrgName,
  456. _sessionContextProvider.SessionContext.OrgIsCenter
  457. );
  458. //创建下一/N个节点(会签汇总节点:会签未全部办理时不创建,最后一个会签办理节点创建会签汇总节点)
  459. var nextSteps = await CreateNextStepsAsync(workflow, currentStep, dto,
  460. nextStepDefine, assigner, isNextDynamic, expiredTime, dto.IsStartCountersign,
  461. isAutoFillSummaryOpinion, stepConfig, cancellationToken);
  462. // //更新办理对象(nextSteps无元素表示当前节点为会签办理节点且当前会签没有全部办理完成)
  463. // workflow.UpdateHandlers(current.RequiredUserId, current.RequiredOrgId,
  464. // flowAssignInfo.FlowAssignType, flowAssignInfo.HandlerObjects, nextSteps.Any());
  465. //指派实际办理节点
  466. UpdateActualStep(workflow, dto, nextStepDefine, nextSteps);
  467. //更新实际办理节点
  468. UpdateCurrentStep(workflow, dto, nextStepDefine, nextSteps);
  469. //更新会签实际办理对象信息
  470. if (currentStep.IsActualHandled)
  471. workflow.AddCsActualHandler(current.UserId, current.OrgId);
  472. await _workflowRepository.UpdateAsync(workflow, cancellationToken);
  473. #endregion
  474. #region 流转记录
  475. //var trace = await NextTraceAsync(workflow, dto, currentStep, cancellationToken);
  476. #endregion
  477. await _publisher.PublishAsync(
  478. new NextStepNotify(workflow, dto, currentTrace, nextStepDefine,
  479. current.OrgId, expiredTime.HasValue), PublishStrategy.ParallelWhenAll,
  480. cancellationToken);
  481. return nextSteps;
  482. }
  483. public async Task<Workflow> GetWorkflowAsync(string workflowId,
  484. bool withDefine = false, bool withSteps = false,
  485. bool withTraces = false, bool withTracesTree = false,
  486. bool withSupplements = false, bool withCountersigns = false,
  487. CancellationToken cancellationToken = default)
  488. {
  489. if (withTraces && withTracesTree)
  490. throw new UserFriendlyException("traces只能在集合与树形集合结构中二选一");
  491. var query = _workflowRepository.Queryable().Where(d => d.Id == workflowId);
  492. if (withDefine)
  493. query = query.Includes(d => d.WorkflowDefinition);
  494. // if (withSupplements)
  495. // query = query.Includes(d => d.Supplements);
  496. //if (withAssigns)
  497. // query = query.Includes(d => d.Assigns);
  498. if (withCountersigns)
  499. query = query.Includes(d => d.Countersigns, x => x.Members);
  500. if (withSteps)
  501. query = query.Includes(d => d.Steps);
  502. if (withTraces)
  503. query = query.Includes(d => d.Traces);
  504. var workflow = await query.FirstAsync(cancellationToken);
  505. if (workflow is null)
  506. throw new UserFriendlyException("无效workflowId");
  507. if (withTracesTree)
  508. {
  509. workflow.Traces = await _workflowTraceRepository.Queryable()
  510. .Where(d => d.WorkflowId == workflow.Id)
  511. .OrderBy(d => d.CreationTime)
  512. .ToTreeAsync(d => d.Traces, d => d.ParentId, null);
  513. }
  514. return workflow;
  515. }
  516. /// <summary>
  517. /// 查询用户对于当前流程权限
  518. /// </summary>
  519. public async Task<(Workflow Workflow, string? CountersignId, bool CanHandle, bool CanPrevious, WorkflowTrace? Trace)>
  520. GetWorkflowHandlePermissionAsync(
  521. string workflowId, string userId, string orgId, string[] roleIds,
  522. CancellationToken cancellationToken = default)
  523. {
  524. var workflow = await GetWorkflowAsync(workflowId,
  525. withSteps: true, withTraces: true, withCountersigns: true,
  526. cancellationToken: cancellationToken);
  527. var canHandle = workflow.IsCanHandle(userId, orgId, roleIds);
  528. var canPrevious = false;
  529. if (canHandle)
  530. {
  531. var currentStep = FindCurrentStepWaitForHandle(workflow, userId, orgId, roleIds);
  532. if (currentStep.Status is not EWorkflowStepStatus.Handled)
  533. {
  534. canPrevious = !(currentStep.IsInCountersign() &&
  535. !currentStep.IsTopCountersignEndStep(workflow.TopCountersignStepId));
  536. }
  537. }
  538. var unhandlePreviousTrace = workflow.Traces.FirstOrDefault(d =>
  539. d.Status is not EWorkflowStepStatus.Handled
  540. );
  541. var unCompletedCountersign = workflow.Countersigns
  542. .FirstOrDefault(d => !d.IsCompleted() && d.StarterOrgId == orgId);
  543. if (unCompletedCountersign is null)
  544. return (workflow, null, canHandle, canPrevious, unhandlePreviousTrace);
  545. //var existCountersignEndStep = workflow.Steps.Exists(d =>
  546. // d.IsCountersignEndStep && d.CountersignStartStepId == unCompletedCountersign.StartStepId);
  547. //return (workflow, existCountersignEndStep ? null : unCompletedCountersign.Id, canPrevious);
  548. return (workflow, unCompletedCountersign?.Id ?? null, canHandle, canPrevious, unhandlePreviousTrace);
  549. }
  550. /// <summary>
  551. /// 受理(接办)
  552. /// </summary>
  553. public async Task<DateTime?> AcceptAsync(Workflow workflow,
  554. string userId, string? userName,
  555. string orgId, string? orgName,
  556. string? orgAreaCode, string? orgAreaName,
  557. CancellationToken cancellationToken)
  558. {
  559. if (!workflow.IsCanHandle(_sessionContextProvider.SessionContext.RequiredUserId, _sessionContextProvider.SessionContext.RequiredOrgId,
  560. _sessionContextProvider.SessionContext.Roles)) return null;
  561. //工单完成以后查看的场景
  562. if (workflow.Status != EWorkflowStatus.Runnable) return null;
  563. var currentStep = GetUnHandleStep(workflow.Steps, _sessionContextProvider.SessionContext.RequiredOrgId,
  564. _sessionContextProvider.SessionContext.RequiredUserId, _sessionContextProvider.SessionContext.Roles);
  565. if (currentStep.Status is not EWorkflowStepStatus.WaitForAccept) return null;
  566. //if (currentStep.Handlers.All(d => d.Key != orgId && d.Key != userId)) return null;
  567. if (currentStep.StepType is EStepType.End)
  568. throw new UserFriendlyException("当前流程已流转到最终步骤");
  569. currentStep.Accept(userId, userName,
  570. orgId, orgName,
  571. orgAreaCode, orgAreaName);
  572. var trace = workflow.Traces.First(d => d.Id == currentStep.Id);
  573. _mapper.Map(currentStep, trace);
  574. currentStep.WorkflowTrace = trace;
  575. await _workflowStepRepository.UpdateNav(currentStep)
  576. .Include(d => d.WorkflowTrace)
  577. .ExecuteCommandAsync();
  578. workflow.ActualHandleStepAcceptTime = currentStep.AcceptTime;
  579. await _workflowRepository.Updateable(workflow).ExecuteCommandAsync(cancellationToken);
  580. return workflow.ActualHandleStepAcceptTime;
  581. }
  582. // /// <summary>
  583. // /// 退回(返回前一节点)
  584. // /// </summary>
  585. // /// <returns></returns>
  586. // public async Task<EFlowDirection> PreviousAsync(Workflow workflow, PreviousWorkflowDto dto,
  587. // string applicantId, string applicantName,
  588. // string applicantOrgId, string applicantOrgName,
  589. // string applicantOrgAreaCode, string applicantOrgAreaName,
  590. // bool applicantIsCenter, string[] applicantRoleIds,
  591. // CancellationToken cancellationToken)
  592. // {
  593. // //ValidatePermission(workflow, operater.OrgId, operater.Id);
  594. //
  595. // var (currentStep, prevStep, countersignStartStep) =
  596. // GetPreviousStep(workflow, applicantId, applicantOrgId, applicantRoleIds);
  597. //
  598. // //保存附件
  599. // if (dto.Files.Any())
  600. // currentStep.FileJson = await _fileRepository.AddFileAsync(
  601. // dto.Files, workflow.ExternalId, currentStep.Id, cancellationToken);
  602. //
  603. // // add prev current to remove list
  604. // var removeSteps = new List<WorkflowStep> { currentStep, prevStep };
  605. //
  606. // if (countersignStartStep is not null)
  607. // {
  608. // //add cs steps to remove list
  609. // SearchCountersignSteps(countersignStartStep, workflow.Steps, ref removeSteps);
  610. //
  611. // //end cs
  612. // var currentCountersign =
  613. // workflow.Countersigns.FirstOrDefault(d => d.Id == countersignStartStep.StartCountersignId);
  614. // if (currentCountersign is null)
  615. // throw new UserFriendlyException(
  616. // $"未查询到对应会签信息,workflowId:{workflow.Id}, countersignId:{currentStep.CountersignId}",
  617. // "无效会签编号");
  618. //
  619. // //结束step会签信息
  620. // countersignStartStep.CountersignEnd();
  621. // await _workflowStepRepository.UpdateAsync(countersignStartStep, cancellationToken);
  622. // //updateSteps.Add(countersignStartStep);
  623. //
  624. // //结束会签
  625. // //currentCountersign.End(currentStep.Id, currentStep.Code, currentStep.BusinessType,
  626. // // current.RequiredUserId, current.UserName,
  627. // // current.RequiredOrgId, current.OrgName,
  628. // // current.OrgAreaCode, current.OrgAreaName);
  629. // currentCountersign.End(currentStep.Id, currentStep.Code, currentStep.BusinessType,
  630. // applicantId, applicantName,
  631. // applicantOrgId, applicantOrgName,
  632. // applicantOrgAreaCode, applicantOrgAreaName);
  633. //
  634. // await _workflowCountersignRepository.UpdateAsync(currentCountersign, cancellationToken);
  635. //
  636. // //update workflow cs status
  637. // if (workflow.CheckIfCountersignOver())
  638. // workflow.EndCountersign();
  639. // }
  640. //
  641. // //update trace
  642. // //var trace = await PreviousTraceAsync(workflow.Id, dto, currentStep,
  643. // // applicantId, applicantName,
  644. // // applicantOrgId, applicantOrgName,
  645. // // applicantOrgAreaCode, applicantOrgAreaName,
  646. // // applicantIsCenter, cancellationToken);
  647. // var trace = workflow.Traces.First(t => t.StepId == currentStep.Id);
  648. // // _mapper.Map(dto, trace);
  649. // trace.FileJson = currentStep.FileJson;
  650. // trace.IsSms = dto.AcceptSms;
  651. // trace.Opinion = dto.Opinion;
  652. //
  653. // //HandleTrace(trace, dto.Opinion, current);
  654. //
  655. // trace.Handle(applicantId, applicantName,
  656. // applicantOrgId, applicantOrgName,
  657. // applicantOrgAreaCode, applicantOrgAreaName,
  658. // applicantIsCenter, EHandleMode.Previous, dto.Opinion);
  659. //
  660. // //await _workflowTraceRepository.UpdateAsync(trace, cancellationToken);
  661. //
  662. // //如果有传入期满时间 新节点为传入的期满时间
  663. // if (dto.ExpiredTime.HasValue)
  664. // prevStep.StepExpiredTime = dto.ExpiredTime;
  665. //
  666. // if (workflow.FlowType == EFlowType.Handle)
  667. // {
  668. // prevStep.FlowAssignType = prevStep.BusinessType is EBusinessType.Seat ? EFlowAssignType.Role :
  669. // prevStep.BusinessType is EBusinessType.Send ? EFlowAssignType.User : EFlowAssignType.Org;
  670. // }
  671. // //甄别退回到最开始节点到部门
  672. // if (workflow.FlowType == EFlowType.Review && workflow.ModuleCode == WorkflowModuleConsts.OrderScreen)
  673. // {
  674. // prevStep.FlowAssignType = prevStep.StepType == EStepType.Start ? EFlowAssignType.Org : prevStep.FlowAssignType;
  675. // }
  676. //
  677. // dto.ReverseFlowStepAssignInfo ??= new ReverseFlowStepAssignInfo(EReverseFlowStepCreationPolicy.OriginStepUser);
  678. // var prevStepDefine = workflow.WorkflowDefinition.FindStepDefine(prevStep.Code);
  679. // var stepAssignInfo = GetStepAssignInfo(dto.ReverseFlowStepAssignInfo, prevStep, prevStepDefine);
  680. //
  681. // //复制上一个节点为待接办
  682. // // var newPrevStep =
  683. // // await DuplicateStepWithTraceAsync(workflow, prevStep, EWorkflowTraceType.Previous, cancellationToken);
  684. // var newPrevStep = DuplicateStep(prevStep, EWorkflowTraceType.Previous, stepAssignInfo, dto.ExpiredTime);
  685. // ////退给派单组节点,需按照平均分配原则派给一个派单员 禅道299 TODO
  686. // //if (dto.ReverseFlowStepAssignInfo != null) //todo 改为按策略判断
  687. // //{
  688. // // var handle = dto.ReverseFlowStepAssignInfo.StepAssignInfo;
  689. // // newPrevStep.Assign(handle.UserId, handle.Username, handle.OrgId, handle.OrgName, handle.RoleId, handle.RoleName);
  690. // //}
  691. //
  692. // await _workflowStepRepository.AddAsync(newPrevStep, cancellationToken);
  693. // await CreateTraceAsync(workflow, newPrevStep, EWorkflowTraceType.Previous, cancellationToken);
  694. //
  695. // //remove workflow.steps
  696. // await _workflowStepRepository.RemoveRangeAsync(removeSteps, cancellationToken);
  697. //
  698. // var stepIds = removeSteps.Select(d => d.Id).ToList();
  699. // var updateTraces = workflow.Traces.Where(d => stepIds.Contains(d.StepId)).ToList();
  700. // await UpdateTracesStateAsync(updateTraces, EWorkflowTraceState.StepRemoveByPrevious, cancellationToken);
  701. //
  702. // if (workflow.Status is EWorkflowStatus.Completed)
  703. // workflow.SetStatusRunnable();
  704. //
  705. // //更新实际办理节点信息
  706. // workflow.UpdateActualStepWhenAssign(newPrevStep, new FlowStepHandler
  707. // {
  708. // UserId = newPrevStep.HandlerId,
  709. // Username = newPrevStep.HandlerName,
  710. // OrgId = newPrevStep.HandlerOrgId,
  711. // OrgName = newPrevStep.HandlerOrgName,
  712. // });
  713. //
  714. // workflow.UpdateCurrentStepWhenAssign(newPrevStep, new FlowStepHandler
  715. // {
  716. // UserId = newPrevStep.HandlerId,
  717. // Username = newPrevStep.HandlerName,
  718. // OrgId = newPrevStep.HandlerOrgId,
  719. // OrgName = newPrevStep.HandlerOrgName,
  720. // });
  721. //
  722. // // //更新流程可办理对象
  723. // // workflow.UpdatePreviousHandlers(applicantId, applicantOrgId, prevStep);
  724. //
  725. // //orgToCenter会触发重新计算期满时间,1.无需审核按当前时间进行计算 2.需审核按审核通过时间计算
  726. // var isOrgToCenter = prevStep.BusinessType is EBusinessType.Send && prevStep.IsOrigin;
  727. //
  728. // await _workflowRepository.UpdateAsync(workflow, cancellationToken);
  729. //
  730. // await _publisher.PublishAsync(new PreviousNotify(workflow, newPrevStep, dto, isOrgToCenter),
  731. // PublishStrategy.ParallelWhenAll, cancellationToken);
  732. //
  733. // return GetFlowDirection(currentStep.BusinessType, prevStep.BusinessType);
  734. // }
  735. /// <summary>
  736. /// 退回(new)
  737. /// </summary>
  738. public async Task<EFlowDirection> PreviousAsync(Workflow workflow, PreviousWorkflowDto dto, OperatorInfo operatorInfo,
  739. CancellationToken cancellationToken)
  740. {
  741. //ValidatePermission(workflow, operater.OrgId, operater.Id);
  742. if (string.IsNullOrEmpty(operatorInfo.UserId)
  743. && string.IsNullOrEmpty(operatorInfo.OrgId)
  744. && !operatorInfo.Roles.Any())
  745. throw new UserFriendlyException("无效当前操作人信息");
  746. var (currentStep, prevStep, countersignStartStep) =
  747. GetPreviousStep(workflow, operatorInfo.UserId, operatorInfo.OrgId, operatorInfo.Roles);
  748. //保存附件
  749. if (dto.Files.Any())
  750. currentStep.FileJson = await _fileRepository.AddFileAsync(dto.Files, workflow.ExternalId, currentStep.Id, cancellationToken);
  751. // add prev current to remove list
  752. var removeSteps = new List<WorkflowStep> { currentStep, prevStep };
  753. if (countersignStartStep is not null)
  754. {
  755. //add cs steps to remove list
  756. SearchCountersignSteps(countersignStartStep, workflow.Steps, ref removeSteps);
  757. //end cs
  758. var currentCountersign =
  759. workflow.Countersigns.FirstOrDefault(d => d.Id == countersignStartStep.StartCountersignId);
  760. if (currentCountersign is null)
  761. throw new UserFriendlyException(
  762. $"未查询到对应会签信息,workflowId:{workflow.Id}, countersignId:{currentStep.CountersignId}",
  763. "无效会签编号");
  764. //结束step会签信息
  765. countersignStartStep.CountersignEnd();
  766. await _workflowStepRepository.UpdateAsync(countersignStartStep, cancellationToken);
  767. //updateSteps.Add(countersignStartStep);
  768. //结束会签
  769. currentCountersign.End(currentStep.Id, currentStep.Code, currentStep.BusinessType,
  770. operatorInfo.UserId, operatorInfo.UserName,
  771. operatorInfo.OrgId, operatorInfo.OrgName,
  772. operatorInfo.OrgAreaCode, operatorInfo.OrgAreaName);
  773. await _workflowCountersignRepository.UpdateAsync(currentCountersign, cancellationToken);
  774. //update workflow cs status
  775. if (workflow.CheckIfCountersignOver())
  776. workflow.EndCountersign();
  777. }
  778. var trace = workflow.Traces.First(t => t.StepId == currentStep.Id);
  779. // _mapper.Map(dto, trace);
  780. trace.FileJson = currentStep.FileJson;
  781. trace.IsSms = dto.AcceptSms;
  782. trace.Opinion = dto.Opinion;
  783. //HandleTrace(trace, dto.Opinion, current);
  784. trace.Handle(operatorInfo.UserId, operatorInfo.UserName,
  785. operatorInfo.OrgId, operatorInfo.OrgName,
  786. operatorInfo.OrgAreaCode, operatorInfo.OrgAreaName,
  787. operatorInfo.OrgIsCenter, EHandleMode.Previous, dto.Opinion);
  788. //如果有传入期满时间 新节点为传入的期满时间
  789. if (dto.ExpiredTime.HasValue)
  790. prevStep.StepExpiredTime = dto.ExpiredTime;
  791. //if (workflow.FlowType == EFlowType.Handle) //该逻辑需放在退回操作前依据业务判断
  792. //{
  793. // prevStep.FlowAssignType = prevStep.BusinessType is EBusinessType.Seat ? EFlowAssignType.Role :
  794. // prevStep.BusinessType is EBusinessType.Send ? EFlowAssignType.User : EFlowAssignType.Org;
  795. //}
  796. dto.ReverseFlowStepAssignInfo ??= new ReverseFlowStepAssignInfo(EReverseFlowStepCreationPolicy.OriginStepUser);
  797. var prevStepDefine = workflow.WorkflowDefinition.FindStepDefine(prevStep.Code);
  798. var stepAssignInfo = GetStepAssignInfo(dto.ReverseFlowStepAssignInfo, prevStep, prevStepDefine);
  799. //复制上一个节点为待接办
  800. var newPrevStep = DuplicateStep(prevStep, EWorkflowTraceType.Previous, stepAssignInfo, dto.ExpiredTime);
  801. ////退给派单组节点,需按照平均分配原则派给一个派单员 禅道299 TODO
  802. //if (dto.ReverseFlowStepAssignInfo != null) //todo 改为按策略判断
  803. //{
  804. // var handle = dto.ReverseFlowStepAssignInfo.StepAssignInfo;
  805. // newPrevStep.Assign(handle.UserId, handle.Username, handle.OrgId, handle.OrgName, handle.RoleId, handle.RoleName);
  806. //}
  807. await _workflowStepRepository.AddAsync(newPrevStep, cancellationToken);
  808. await CreateTraceAsync(workflow, newPrevStep, EWorkflowTraceType.Previous, cancellationToken);
  809. //remove workflow.steps
  810. await _workflowStepRepository.RemoveRangeAsync(removeSteps, cancellationToken);
  811. var stepIds = removeSteps.Select(d => d.Id).ToList();
  812. var updateTraces = workflow.Traces.Where(d => stepIds.Contains(d.StepId)).ToList();
  813. await UpdateTracesStateAsync(updateTraces, EWorkflowTraceState.StepRemoveByPrevious, cancellationToken);
  814. if (workflow.Status is EWorkflowStatus.Completed)
  815. workflow.SetStatusRunnable();
  816. //更新实际办理节点信息
  817. workflow.UpdateActualStepWhenAssign(newPrevStep, new FlowStepHandler
  818. {
  819. UserId = newPrevStep.HandlerId,
  820. Username = newPrevStep.HandlerName,
  821. OrgId = newPrevStep.HandlerOrgId,
  822. OrgName = newPrevStep.HandlerOrgName,
  823. });
  824. workflow.UpdateCurrentStepWhenAssign(newPrevStep, new FlowStepHandler
  825. {
  826. UserId = newPrevStep.HandlerId,
  827. Username = newPrevStep.HandlerName,
  828. OrgId = newPrevStep.HandlerOrgId,
  829. OrgName = newPrevStep.HandlerOrgName,
  830. });
  831. // //更新流程可办理对象
  832. // workflow.UpdatePreviousHandlers(applicantId, applicantOrgId, prevStep);
  833. //orgToCenter会触发重新计算期满时间,1.无需审核按当前时间进行计算 2.需审核按审核通过时间计算
  834. var isOrgToCenter = prevStep.BusinessType is EBusinessType.Send && prevStep.IsOrigin;
  835. await _workflowRepository.UpdateAsync(workflow, cancellationToken);
  836. await _publisher.PublishAsync(new PreviousNotify(workflow, newPrevStep, dto, isOrgToCenter),
  837. PublishStrategy.ParallelWhenAll, cancellationToken);
  838. return GetFlowDirection(currentStep.BusinessType, prevStep.BusinessType);
  839. }
  840. private async Task UpdateTracesStateAsync(List<WorkflowTrace> traces, EWorkflowTraceState traceState,
  841. CancellationToken cancellationToken)
  842. {
  843. foreach (var trace in traces)
  844. {
  845. trace.TraceState = traceState;
  846. }
  847. await _workflowTraceRepository.UpdateRangeAsync(traces, cancellationToken);
  848. }
  849. /// <summary>
  850. /// 查询退回节点信息
  851. /// </summary>
  852. public (WorkflowStep currentStep, WorkflowStep prevStep, WorkflowStep? countersignStartStep) GetPreviousStep(
  853. Workflow workflow, string operaterId, string operaterOrgId, string[] roleIds)
  854. {
  855. var currentStep = GetUnHandleStep(workflow.Steps, operaterOrgId, operaterId, roleIds);
  856. var isCurrentTopCountersignEndStep = workflow.IsInCountersign &&
  857. currentStep.IsTopCountersignEndStep(workflow.TopCountersignStepId);
  858. if (currentStep.IsInCountersign() && !isCurrentTopCountersignEndStep)
  859. throw UserFriendlyException.SameMessage("会签节点不支持退回");
  860. if (workflow.FlowType is EFlowType.Review && currentStep.StepType is EStepType.Start &&
  861. currentStep.IsOrigin)
  862. throw UserFriendlyException.SameMessage("当前流程已退回到开始节点");
  863. //当退回操作遇到会签时,删除所有会签节点直达topCsStep
  864. //find prevStep, update handler
  865. WorkflowStep? prevStep, countersignStartStep = null;
  866. if (isCurrentTopCountersignEndStep)
  867. {
  868. //prev is topstart's prev
  869. countersignStartStep = workflow.Steps.FirstOrDefault(d => d.Id == currentStep.CountersignStartStepId);
  870. if (countersignStartStep is null)
  871. throw new UserFriendlyException("未查询到对应会签开始节点");
  872. prevStep = workflow.Steps.FirstOrDefault(d => d.Id == countersignStartStep.PrevStepId);
  873. }
  874. else
  875. {
  876. prevStep = workflow.Steps.FirstOrDefault(d => d.Id == currentStep.PrevStepId);
  877. }
  878. if (prevStep == null)
  879. throw UserFriendlyException.SameMessage("未查询到前一节点");
  880. while (prevStep.IsCountersignEndStep)
  881. {
  882. countersignStartStep = workflow.Steps.FirstOrDefault(d => d.Id == prevStep.CountersignStartStepId);
  883. prevStep = countersignStartStep ?? throw new UserFriendlyException("未查询到对应会签开始节点");
  884. if (prevStep == null)
  885. throw UserFriendlyException.SameMessage("未查询到前一节点");
  886. }
  887. return (currentStep, prevStep, countersignStartStep);
  888. }
  889. /// <summary>
  890. /// 查询派单池中流程节点id
  891. /// </summary>
  892. public async Task<IReadOnlyList<string>> GetUnhandleStepIdsFromSendPoolAsync(string sendPoolId,
  893. CancellationToken cancellationToken)
  894. {
  895. return await _workflowStepRepository.Queryable()
  896. .Where(d => SqlFunc.JsonListObjectAny(d.Handlers, "Key", sendPoolId))
  897. .Select(d => d.Id)
  898. .ToListAsync(cancellationToken);
  899. }
  900. /// <summary>
  901. /// 查询归属某用户的所有流程节点
  902. /// </summary>
  903. public async Task<List<WorkflowStep>> GetStepsBelongsToAsync(string userId, CancellationToken cancellationToken)
  904. {
  905. return await _workflowStepRepository.Queryable()
  906. .Includes(d => d.WorkflowTrace)
  907. .Where(d => d.HandlerId == userId)
  908. .OrderBy(d => d.CreationTime)
  909. .ToListAsync(cancellationToken);
  910. }
  911. /// <summary>
  912. /// 批量修改工单办理对象
  913. /// </summary>
  914. public async Task ChangeHandlerBatchAsync(
  915. IReadOnlyList<(string userId, string username, string orgId, string orgName, string? roleId, string? roleName, ICollection<WorkflowStep>
  916. steps)> handlers,
  917. CancellationToken cancellationToken)
  918. {
  919. foreach (var handler in handlers)
  920. {
  921. foreach (var step in handler.steps)
  922. {
  923. step.FlowAssignType = EFlowAssignType.User;
  924. step.Assign(handler.userId, handler.username,
  925. handler.orgId, handler.orgName, handler.roleId, handler.roleName);
  926. if (step.WorkflowTrace is null)
  927. throw new UserFriendlyException("未查询节点对应快照信息");
  928. step.WorkflowTrace.FlowAssignType = EFlowAssignType.User;
  929. step.WorkflowTrace.Assign(handler.userId, handler.username,
  930. handler.orgId, handler.orgName, handler.roleId, handler.roleName);
  931. //更新节点CreationTime 派单量统计 待派单数据使用
  932. step.CreationTime = DateTime.Now;
  933. }
  934. }
  935. var steps = handlers.SelectMany(d => d.steps).ToList();
  936. //await _workflowStepRepository.UpdateRangeAsync(steps, cancellationToken);
  937. await _workflowStepRepository.UpdateNav(steps)
  938. .Include(d => d.WorkflowTrace)
  939. .ExecuteCommandAsync();
  940. }
  941. /// <summary>
  942. /// 查询工单办理中的一级部门
  943. /// </summary>
  944. public async Task<ICollection<Kv>> GetLevelOneOrgsAsync(string workflowId, CancellationToken cancellation)
  945. {
  946. var traces = await _workflowTraceRepository.Queryable()
  947. .LeftJoin<SystemOrganize>((t, o) => t.HandlerOrgId == o.Id)
  948. .Where((t, o) => t.WorkflowId == workflowId &&
  949. !string.IsNullOrEmpty(t.HandlerOrgId) &&
  950. o.Level == 1)
  951. .ToListAsync(cancellation);
  952. //var handlers = await _workflowStepHandlerRepository.Queryable()
  953. // .InnerJoin<WorkflowTrace>((wsh, wt) => wsh.WorkflowStepId == wt.StepId)
  954. // .LeftJoin<SystemOrganize>((wsh, wt, o) => wsh.OrgId == o.Id)
  955. // .Where((wsh, wt, o) => wsh.WorkflowId == workflowId &&
  956. // //wt.BusinessType == EBusinessType.Department &&
  957. // //wt.HandlerType == EHandlerType.OrgLevel &&
  958. // !string.IsNullOrEmpty(wsh.OrgId) &&
  959. // o.Level == 1)
  960. // .ToListAsync(cancellation);
  961. //var orgs = handlers.Select(d => new Kv(d.OrgId, d.OrgName))
  962. // .DistinctBy(d => d.Key)
  963. // .ToList();
  964. var orgs = traces
  965. .DistinctBy(d => d.HandlerOrgId)
  966. .Select(d => new Kv(d.HandlerOrgId, d.HandlerOrgName))
  967. .ToList();
  968. return orgs;
  969. //var workflow = await GetWorkflowAsync(workflowId, withSteps: true, cancellationToken: cancellation);
  970. //var list = workflow.Steps.Distinct().Where(d => d.BusinessType == EBusinessType.Department &&
  971. // d.HandlerType == EHandlerType.OrgLevel &&
  972. // d.StepHandlers.Any(d =>
  973. // !string.IsNullOrEmpty(d.OrgId) && d.OrgId.CheckIfOrgLevelIs(1)))
  974. // .Select(d => new Kv(d.StepHandlers.First().OrgId, d.StepHandlers.First().OrgName))
  975. // .ToList();
  976. //return list.Where((x, i) => list.FindIndex(z => z.Key == x.Key) == i).ToList();
  977. }
  978. /// <summary>
  979. /// 更新未办理节点的期满时间
  980. /// </summary>
  981. public async Task UpdateUnhandleExpiredTimeAsync(string workflowId, DateTime expiredTime,
  982. CancellationToken cancellation)
  983. {
  984. var steps = await _workflowStepRepository.Queryable()
  985. .Includes(d => d.WorkflowTrace)
  986. .Where(d => d.WorkflowId == workflowId &&
  987. d.Status < EWorkflowStepStatus.Handled)
  988. .ToListAsync(cancellation);
  989. foreach (var step in steps)
  990. {
  991. step.StepExpiredTime = expiredTime;
  992. step.WorkflowTrace.StepExpiredTime = expiredTime;
  993. }
  994. await _workflowStepRepository.UpdateNav(steps)
  995. .Include(d => d.WorkflowTrace)
  996. .ExecuteCommandAsync();
  997. }
  998. /// <summary>
  999. /// 查询该部门最后办理节点
  1000. /// </summary>
  1001. /// <returns></returns>
  1002. public async Task<WorkflowStep> FindLastHandleStepAsync(string workflowId, string orgId, CancellationToken cancellation)
  1003. {
  1004. return await _workflowStepRepository.Queryable()
  1005. .Where(d => d.WorkflowId == workflowId
  1006. && d.HandlerOrgId == orgId
  1007. && d.StepType != EStepType.End
  1008. && d.StepType != EStepType.Summary
  1009. && !d.IsCountersignEndStep)
  1010. //.Where(d => d.StepHandlers.Any(sh => sh.OrgId == orgId) && d.WorkflowId == workflowId)
  1011. .OrderByDescending(d => d.HandleTime)
  1012. .FirstAsync(cancellation);
  1013. }
  1014. /// <summary>
  1015. /// 部门会签工单获取流程最顶级办理节点
  1016. /// </summary>
  1017. /// <returns></returns>
  1018. public async Task<WorkflowStep> FindTopHandleStepAsync(string workflowId, CancellationToken cancellation)
  1019. {
  1020. var workflow = await GetWorkflowAsync(workflowId, withSteps: true, cancellationToken: cancellation);
  1021. return workflow.Steps.FirstOrDefault(x => x.Id == workflow.TopCountersignStepId);
  1022. }
  1023. /// <summary>
  1024. /// 查询流转方向
  1025. /// </summary>
  1026. public EFlowDirection GetFlowDirection(EBusinessType sourceStepBusinessType,
  1027. EBusinessType directionStepBusinessType)
  1028. {
  1029. switch (sourceStepBusinessType)
  1030. {
  1031. case EBusinessType.Seat:
  1032. case EBusinessType.Send:
  1033. return directionStepBusinessType switch
  1034. {
  1035. EBusinessType.Seat => EFlowDirection.CenterToCenter,
  1036. EBusinessType.Send => EFlowDirection.CenterToCenter,
  1037. EBusinessType.Department => EFlowDirection.CenterToOrg,
  1038. EBusinessType.DepartmentLeader => EFlowDirection.CenterToOrg,
  1039. EBusinessType.File => EFlowDirection.CenterToFile,
  1040. _ => throw new ArgumentOutOfRangeException(nameof(directionStepBusinessType),
  1041. directionStepBusinessType, null)
  1042. };
  1043. case EBusinessType.Department:
  1044. case EBusinessType.DepartmentLeader:
  1045. return directionStepBusinessType switch
  1046. {
  1047. EBusinessType.Seat => EFlowDirection.OrgToCenter,
  1048. EBusinessType.Send => EFlowDirection.OrgToCenter,
  1049. EBusinessType.Department => EFlowDirection.OrgToOrg,
  1050. EBusinessType.DepartmentLeader => EFlowDirection.OrgToOrg,
  1051. EBusinessType.File => EFlowDirection.OrgToFile,
  1052. _ => throw new ArgumentOutOfRangeException(nameof(directionStepBusinessType),
  1053. directionStepBusinessType, null)
  1054. };
  1055. case EBusinessType.File:
  1056. return directionStepBusinessType switch
  1057. {
  1058. EBusinessType.Seat => EFlowDirection.FiledToCenter,
  1059. EBusinessType.Send => EFlowDirection.FiledToCenter,
  1060. EBusinessType.Department => EFlowDirection.FiledToOrg,
  1061. EBusinessType.DepartmentLeader => EFlowDirection.FiledToOrg,
  1062. _ => throw new ArgumentOutOfRangeException(nameof(directionStepBusinessType), directionStepBusinessType, null)
  1063. };
  1064. default:
  1065. throw new ArgumentOutOfRangeException(nameof(sourceStepBusinessType), sourceStepBusinessType, null);
  1066. }
  1067. }
  1068. /// <summary>
  1069. /// 流程被签收至某个用户(更新流转对象,办理对象,节点办理对象以及stepHandlers)
  1070. /// </summary>
  1071. public async Task<Workflow> SignToSomebodyAsync(string workflowId, string userId, string username, string orgId,
  1072. string orgName, CancellationToken cancellationToken)
  1073. {
  1074. var workflow = await GetWorkflowAsync(workflowId, withSteps: true, withTraces: true,
  1075. cancellationToken: cancellationToken);
  1076. // workflow.Assign(EFlowAssignType.User, _sessionContextProvider.SessionContext.RequiredUserId);
  1077. //
  1078. // workflow.HandlerOrgs = new();
  1079. // workflow.HandlerUsers = new List<HandlerGroupItem>
  1080. // {
  1081. // new()
  1082. // {
  1083. // GroupId = Guid.NewGuid().ToString(),
  1084. // Key = userId,
  1085. // Value = username
  1086. // }
  1087. // };
  1088. var startStep = workflow.Steps.First(d => d.StepType == EStepType.Start && d.IsOrigin);
  1089. startStep.Handlers = new List<Kv> { new(userId, username) };
  1090. startStep.AcceptorId = userId;
  1091. startStep.AcceptorName = username;
  1092. startStep.AcceptTime = DateTime.Now;
  1093. startStep.AcceptorOrgId = orgId;
  1094. startStep.AcceptorOrgName = orgName;
  1095. startStep.FlowAssignType = EFlowAssignType.User;
  1096. startStep.Assign(userId, username, orgId, orgName);
  1097. //var stepHandler = startStep.StepHandlers.First();
  1098. //startStep.StepHandlers.RemoveAll(d => d.Id != stepHandler.Id);
  1099. //stepHandler.UserId = userId;
  1100. //stepHandler.Username = username;
  1101. //stepHandler.OrgId = orgId;
  1102. //stepHandler.OrgName = orgName;
  1103. startStep.WorkflowTrace = workflow.Traces.First(d => d.Id == startStep.Id);
  1104. _mapper.Map(startStep, startStep.WorkflowTrace);
  1105. await _workflowStepRepository.UpdateNav(startStep)
  1106. //.Include(d => d.StepHandlers)
  1107. .Include(d => d.WorkflowTrace)
  1108. .ExecuteCommandAsync();
  1109. await _workflowRepository.UpdateAsync(workflow, cancellationToken);
  1110. return workflow;
  1111. }
  1112. /// <summary>
  1113. /// 非节点办理人员查询待办节点
  1114. /// </summary>
  1115. /// <returns></returns>
  1116. public async Task<ICollection<WorkflowStep>> GetUnhandleStepsByOthersAsync(string workflowId, CancellationToken cancellationToken)
  1117. {
  1118. return await _workflowStepRepository.Queryable()
  1119. .Where(d => d.WorkflowId == workflowId && d.Status != EWorkflowStepStatus.Handled)
  1120. .ToListAsync(cancellationToken);
  1121. }
  1122. /// <summary>
  1123. /// 根据汇总对象id找到被汇总节点,生成指派到用户的办理对象
  1124. /// </summary>
  1125. /// <param name="workflow"></param>
  1126. /// <param name="stepDefine"></param>
  1127. /// <returns></returns>
  1128. public FlowStepHandler GetSummaryTargetFlowStepHandler(Workflow workflow, string summaryTargetStepCode)
  1129. {
  1130. //根据汇总对象id找到被汇总节点
  1131. var summaryTargetStep = workflow.Steps.FirstOrDefault(d =>
  1132. d.StepType == EStepType.Normal &&
  1133. d.Code == summaryTargetStepCode &&
  1134. d.Status == EWorkflowStepStatus.Handled &&
  1135. d.IsOrigin);
  1136. if (summaryTargetStep is null)
  1137. throw UserFriendlyException.SameMessage("未查询到汇总对象节点");
  1138. var handler = new FlowStepHandler
  1139. {
  1140. Key = summaryTargetStep.HandlerId,
  1141. Value = summaryTargetStep.HandlerName,
  1142. UserId = summaryTargetStep.HandlerId,
  1143. Username = summaryTargetStep.HandlerName,
  1144. OrgId = summaryTargetStep.HandlerOrgId,
  1145. OrgName = summaryTargetStep.HandlerOrgName,
  1146. RoleId = summaryTargetStep.RoleId,
  1147. RoleName = summaryTargetStep.RoleName
  1148. };
  1149. return handler;
  1150. }
  1151. /// <summary>
  1152. /// 追加归档信息(接收ds推送12315归档信息)
  1153. /// </summary>
  1154. public async Task AppendFileOpinionAsync(string workflowId, string opinion, List<FileDto> files, CancellationToken cancellationToken)
  1155. {
  1156. //归档意见,附件追加在归档节点前一节点上
  1157. var workflow = await GetWorkflowAsync(workflowId, withSteps: true, withTraces: true, cancellationToken: cancellationToken);
  1158. var endStep = workflow.Steps.FirstOrDefault(d => d.StepType == EStepType.End);
  1159. if (endStep is null)
  1160. throw new UserFriendlyException($"该流程还未归档, workflowId: {workflowId}");
  1161. var prevStep = workflow.Steps.FirstOrDefault(d => d.Id == endStep.PrevStepId);
  1162. if (prevStep is null)
  1163. throw new UserFriendlyException($"未找到归档节点的前一节点, workflowId: {workflowId}, endStepId: {endStep.Id}");
  1164. prevStep.Opinion = opinion;
  1165. if (files != null && files.Any())
  1166. {
  1167. var filejsons = await _fileRepository.AddFileAsync(files, workflow.ExternalId, prevStep.Id, cancellationToken);
  1168. prevStep.FileJson.AddRange(filejsons);
  1169. }
  1170. await _workflowStepRepository.UpdateAsync(prevStep, cancellationToken);
  1171. var endTrace = workflow.Traces.FirstOrDefault(p => p.Id == prevStep.Id);
  1172. if (endTrace != null)
  1173. {
  1174. endTrace.Opinion = prevStep.Opinion;
  1175. endTrace.FileJson = prevStep.FileJson;
  1176. await _workflowTraceRepository.UpdateAsync(endTrace, cancellationToken);
  1177. }
  1178. }
  1179. /// <summary>
  1180. /// 跳转至结束节点(无视流程模板配置以及当前办理对象,直接跳至结束节点)
  1181. /// </summary>
  1182. public async Task JumpToEndAsync(ISessionContext current, string workflowId, string opinion, List<FileDto> files, DateTime? expiredTime,
  1183. EReviewResult reviewResult = EReviewResult.Unknown, CancellationToken cancellationToken = default)
  1184. {
  1185. var workflow = await GetWorkflowAsync(workflowId, withDefine: true, withSteps: true, withTraces: true,
  1186. withCountersigns: true, cancellationToken: cancellationToken);
  1187. await JumpToEndAsync(current, workflow, opinion, files, expiredTime, reviewResult, cancellationToken);
  1188. }
  1189. /// <summary>
  1190. /// 跳转至结束节点(无视流程模板配置以及当前办理对象,直接跳至结束节点)
  1191. /// </summary>
  1192. public async Task JumpToEndAsync(ISessionContext current, Workflow workflow, string opinion, List<FileDto> files, DateTime? expiredTime,
  1193. EReviewResult reviewResult = EReviewResult.Unknown, CancellationToken cancellationToken = default)
  1194. {
  1195. var endStepDefine = workflow.WorkflowDefinition.FindEndStepDefine();
  1196. if (endStepDefine is null)
  1197. throw new UserFriendlyException("未正确配置结束节点");
  1198. var dto = new BasicWorkflowDto
  1199. {
  1200. NextStepCode = endStepDefine.Code,
  1201. NextStepName = endStepDefine.Name,
  1202. FlowDirection = EFlowDirection.OrgToFile,
  1203. BusinessType = endStepDefine.BusinessType,
  1204. ReviewResult = reviewResult,
  1205. Opinion = opinion,
  1206. Files = files
  1207. };
  1208. var unhandleSteps = workflow.Steps
  1209. .Where(d => d.Status != EWorkflowStepStatus.Handled).ToList();
  1210. var unhandleTraces = workflow.Traces
  1211. .Where(d => d.Status != EWorkflowStepStatus.Handled).ToList();
  1212. //get currentStep
  1213. var currentStep = unhandleSteps.MaxBy(d => d.CreationTime)
  1214. ?? workflow.Steps.MaxBy(d => d.CreationTime);
  1215. foreach (var step in unhandleSteps)
  1216. {
  1217. await HandleStepAsync(step, workflow, dto, null, null, EHandleMode.Normal, cancellationToken);
  1218. if (step.IsStartCountersign)
  1219. step.CountersignEnd();
  1220. var trace = unhandleTraces.First(d => d.StepId == step.Id);
  1221. _mapper.Map(dto, trace);
  1222. _mapper.Map(step, trace);
  1223. }
  1224. await _workflowStepRepository.UpdateRangeAsync(unhandleSteps, cancellationToken);
  1225. await _workflowTraceRepository.UpdateRangeAsync(unhandleTraces, cancellationToken);
  1226. //结束会签
  1227. var counstersigns = workflow.Countersigns
  1228. .Where(d => !d.EndTime.HasValue)
  1229. .ToList();
  1230. foreach (var counstersign in counstersigns)
  1231. {
  1232. //结束会签
  1233. counstersign.End(currentStep.Id, currentStep.Code, currentStep.BusinessType,
  1234. current.UserId, current.UserName,
  1235. current.OrgId, current.OrgName,
  1236. current.OrgAreaCode, current.OrgAreaName);
  1237. }
  1238. await _workflowCountersignRepository.UpdateRangeAsync(counstersigns, cancellationToken);
  1239. //更新实际办理节点信息
  1240. if (currentStep.StepType != EStepType.Summary && currentStep.StepType != EStepType.End)
  1241. {
  1242. workflow.UpdateActualStepWhenHandle(currentStep, current.OrgAreaCode, current.OrgAreaName, current.OrgLevel);
  1243. workflow.ActualHandleStepAcceptTime = currentStep.AcceptTime;
  1244. }
  1245. //
  1246. // workflow.UpdateCurrentStepWhenHandle(currentStep, current.OrgAreaCode, current.OrgAreaName, current.OrgLevel);
  1247. if (workflow.Steps.All(d => d.StepType != EStepType.End))
  1248. {
  1249. await EndAsync(current, workflow, dto, endStepDefine, currentStep, expiredTime, cancellationToken);
  1250. }
  1251. }
  1252. /// <summary>
  1253. /// 查找当前会签内所有节点(含start,end)
  1254. /// </summary>
  1255. private void SearchCountersignSteps(WorkflowStep startStep, List<WorkflowStep> steps,
  1256. ref List<WorkflowStep> csSteps)
  1257. {
  1258. if (startStep.IsStartCountersign)
  1259. {
  1260. var countersignSteps = steps.Where(d => d.CountersignId == startStep.StartCountersignId).ToList();
  1261. if (countersignSteps.Any())
  1262. {
  1263. foreach (var countersignStep in countersignSteps)
  1264. {
  1265. SearchCountersignSteps(countersignStep, steps, ref csSteps);
  1266. }
  1267. }
  1268. }
  1269. csSteps.Add(startStep);
  1270. }
  1271. /// <summary>
  1272. /// 撤回(返回到之前任意节点)
  1273. /// </summary>
  1274. public async Task<bool> RecallAsync(RecallDto dto, ReverseFlowStepAssignInfo reverseFlowStepAssignInfo,
  1275. EWorkflowTraceType traceType, DateTime? expiredTime, bool isOrderFiled, EHandleMode handleMode,
  1276. Action<WorkflowStep>? stepConfig = null, CancellationToken cancellationToken = default)
  1277. {
  1278. var workflow = await GetWorkflowAsync(dto.WorkflowId, withDefine: true, withSteps: true,
  1279. withTraces: true, withCountersigns: true, cancellationToken: cancellationToken);
  1280. var targetStepDefine = GetStepDefine(workflow.WorkflowDefinition, dto.NextStepCode);
  1281. if (targetStepDefine.StepType is EStepType.End)
  1282. throw UserFriendlyException.SameMessage("结束节点不支持撤回");
  1283. var targetStep = workflow.Steps.FirstOrDefault(d => d.Code == dto.NextStepCode && d.IsOrigin);
  1284. if (targetStep is null)
  1285. throw UserFriendlyException.SameMessage("该流程尚未流转至该节点");
  1286. return await RecallAsync(workflow, dto, reverseFlowStepAssignInfo, targetStepDefine, targetStep, traceType,
  1287. expiredTime, isOrderFiled, handleMode, stepConfig, cancellationToken);
  1288. }
  1289. /// <summary>
  1290. /// 撤回(返回到之前任意节点)
  1291. /// </summary>
  1292. public Task<bool> RecallAsync(Workflow workflow, RecallDto dto, ReverseFlowStepAssignInfo reverseFlowStepAssignInfo,
  1293. StepDefine targetStepDefine, EWorkflowTraceType traceType, DateTime? expiredTime, bool isOrderFiled,
  1294. EHandleMode handleMode, Action<WorkflowStep>? stepConfig = null, CancellationToken cancellationToken = default)
  1295. {
  1296. var targetStep = workflow.Steps.FirstOrDefault(d => d.Code == dto.NextStepCode && d.IsOrigin);
  1297. if (targetStep is null)
  1298. throw UserFriendlyException.SameMessage("该流程尚未流转至该节点");
  1299. return RecallAsync(workflow, dto, reverseFlowStepAssignInfo, targetStepDefine, targetStep,
  1300. traceType, expiredTime, isOrderFiled, handleMode, stepConfig, cancellationToken);
  1301. }
  1302. public Task<bool> RecallAsync(Workflow workflow, RecallDto dto, ReverseFlowStepAssignInfo reverseFlowStepAssignInfo,
  1303. WorkflowStep targetStep, EWorkflowTraceType traceType, DateTime? expiredTime, bool isOrderFiled,
  1304. EHandleMode handleMode, Action<WorkflowStep>? stepConfig = null, CancellationToken cancellationToken = default)
  1305. {
  1306. var targetStepDefine = GetStepDefine(workflow.WorkflowDefinition, dto.NextStepCode);
  1307. if (targetStepDefine.StepType is EStepType.End)
  1308. throw UserFriendlyException.SameMessage("结束节点不支持撤回");
  1309. return RecallAsync(workflow, dto, reverseFlowStepAssignInfo,
  1310. targetStepDefine, targetStep, traceType,
  1311. expiredTime, isOrderFiled, handleMode, stepConfig, cancellationToken);
  1312. }
  1313. public async Task<bool> RecallAsync(Workflow workflow, RecallDto dto, ReverseFlowStepAssignInfo reverseFlowStepAssignInfo,
  1314. StepDefine targetStepDefine, WorkflowStep targetStep, EWorkflowTraceType traceType,
  1315. DateTime? expiredTime, bool isOrderFiled, EHandleMode handleMode, Action<WorkflowStep>? stepConfig = null,
  1316. CancellationToken cancellationToken = default)
  1317. {
  1318. var targetIsStartStep = targetStepDefine.StepType is EStepType.Start;
  1319. var updateTraces = new List<WorkflowTrace>();
  1320. //update uncomplete traces
  1321. var uncompleteTraces = workflow.Traces.Where(d => d.Status != EWorkflowStepStatus.Handled).ToList();
  1322. if (uncompleteTraces.Any())
  1323. {
  1324. foreach (var trace in uncompleteTraces)
  1325. {
  1326. trace.Handle(
  1327. _sessionContextProvider.SessionContext.RequiredUserId, _sessionContextProvider.SessionContext.UserName,
  1328. _sessionContextProvider.SessionContext.RequiredOrgId, _sessionContextProvider.SessionContext.OrgName,
  1329. _sessionContextProvider.SessionContext.OrgAreaCode, _sessionContextProvider.SessionContext.OrgAreaName,
  1330. _sessionContextProvider.SessionContext.OrgIsCenter, handleMode, dto.Opinion);
  1331. }
  1332. //await _workflowTraceRepository.UpdateRangeAsync(uncompleteTraces, cancellationToken);
  1333. updateTraces.AddRange(uncompleteTraces);
  1334. }
  1335. else
  1336. {
  1337. var endTrace = workflow.Traces.Where(d => d.StepType == EStepType.End).MaxBy(d => d.CreationTime);
  1338. if (endTrace is not null)
  1339. {
  1340. endTrace.Opinion += ("\r\n" + dto.Opinion);
  1341. updateTraces.Add(endTrace);
  1342. }
  1343. }
  1344. //get targetStep's previous
  1345. WorkflowStep? targetPrevStep = null;
  1346. if (!targetIsStartStep)
  1347. {
  1348. targetPrevStep = workflow.Steps.FirstOrDefault(d => d.Id == targetStep.PrevStepId);
  1349. if (targetPrevStep == null)
  1350. throw new UserFriendlyException($"{nameof(RecallAsync)}, 未找到目标节点的前一节点, flowId: {workflow.Id}");
  1351. }
  1352. //查询所有目标节点之后的节点,然后删掉(包括目标节点)
  1353. var removeSteps = GetStepsBehindTargetStep(workflow.Steps, targetStep);
  1354. if (removeSteps.Any())
  1355. {
  1356. await _workflowStepRepository.RemoveRangeAsync(removeSteps, cancellationToken);
  1357. workflow.Steps.RemoveAll(d => removeSteps.Contains(d));
  1358. //更新快照对应节点状态
  1359. var stepIds = removeSteps.Select(d => d.Id).ToList();
  1360. var traces = workflow.Traces.Where(d => stepIds.Contains(d.StepId)).ToList();
  1361. //await UpdateTracesStateAsync(updateTraces, EWorkflowTraceState.StepRemoveByRecall, cancellationToken);
  1362. foreach (var trace in traces)
  1363. {
  1364. trace.TraceState = isOrderFiled
  1365. ? EWorkflowTraceState.StepRemoveByRecallWhenFiled
  1366. : EWorkflowTraceState.StepRemoveByRecall;
  1367. }
  1368. updateTraces.AddRange(traces);
  1369. }
  1370. await _workflowTraceRepository.UpdateRangeAsync(updateTraces, cancellationToken);
  1371. //结束会签
  1372. var unCompleteCountersigns = workflow.Countersigns.Where(d => !d.IsCompleted()).ToList();
  1373. if (unCompleteCountersigns.Any())
  1374. {
  1375. foreach (var unCompleteCountersign in unCompleteCountersigns)
  1376. {
  1377. unCompleteCountersign.End(null, null, EBusinessType.File,
  1378. _sessionContextProvider.SessionContext.RequiredUserId, _sessionContextProvider.SessionContext.UserName,
  1379. _sessionContextProvider.SessionContext.RequiredOrgId, _sessionContextProvider.SessionContext.OrgName,
  1380. _sessionContextProvider.SessionContext.OrgAreaCode, _sessionContextProvider.SessionContext.OrgAreaName);
  1381. }
  1382. await _workflowCountersignRepository.UpdateRangeAsync(unCompleteCountersigns, cancellationToken);
  1383. }
  1384. workflow.EndCountersign();
  1385. workflow.ResetOption();
  1386. if (workflow.Status is EWorkflowStatus.Completed)
  1387. workflow.SetStatusRunnable();
  1388. var assigner = new UserInfo(
  1389. _sessionContextProvider.SessionContext.UserId,
  1390. _sessionContextProvider.SessionContext.UserName,
  1391. _sessionContextProvider.SessionContext.OrgId,
  1392. _sessionContextProvider.SessionContext.OrgName,
  1393. _sessionContextProvider.SessionContext.OrgIsCenter
  1394. );
  1395. //var targetStepNew = targetIsStartStep
  1396. // ? await CreateStartStepAsync(workflow, targetStepDefine, dto, assigner,
  1397. // dto.NextHandlers.First(), traceType, expiredTime, flowAssignInfo.FlowAssignType, cancellationToken)
  1398. // : (await CreateStepsAsync(workflow, targetStepDefine, targetPrevStep, dto, assigner,
  1399. // flowAssignInfo.FlowAssignType, dto.NextHandlers,
  1400. // null, EWorkflowStepStatus.WaitForAccept, ECountersignPosition.None, true, traceType,
  1401. // null, expiredTime, cancellationToken: cancellationToken)).First();
  1402. var stepAssignInfo = GetStepAssignInfo(reverseFlowStepAssignInfo, targetStep, targetStepDefine);
  1403. var targetStepNew = targetIsStartStep
  1404. ? await CreateStartStepAsync(workflow, targetStepDefine, dto, assigner,
  1405. stepAssignInfo, traceType, expiredTime, stepAssignInfo.FlowAssignType, cancellationToken)
  1406. : (await CreateStepsAsync(workflow, targetStepDefine, targetPrevStep, dto, assigner, [stepAssignInfo],
  1407. null, EWorkflowStepStatus.WaitForAccept, ECountersignPosition.None, true, traceType,
  1408. null, expiredTime, stepConfig: stepConfig, cancellationToken: cancellationToken)).First();
  1409. //更新实际办理节点信息
  1410. workflow.UpdateActualStepWhenAssign(targetStepNew, new FlowStepHandler
  1411. {
  1412. UserId = targetStep.HandlerId,
  1413. Username = targetStep.HandlerName,
  1414. OrgId = targetStep.HandlerOrgId,
  1415. OrgName = targetStep.HandlerOrgName
  1416. });
  1417. workflow.UpdateCurrentStepWhenAssign(targetStepNew, new FlowStepHandler
  1418. {
  1419. UserId = targetStep.HandlerId,
  1420. Username = targetStep.HandlerName,
  1421. OrgId = targetStep.HandlerOrgId,
  1422. OrgName = targetStep.HandlerOrgName
  1423. });
  1424. //取消维护workflow得冗余字段(FlowedOrgIds, HandlerOrgs)
  1425. //workflow.Assign(flowAssignInfo.FlowAssignType, flowAssignInfo.GetHandlerIds());
  1426. //workflow.ResetHandlers(flowAssignInfo.FlowAssignType, flowAssignInfo.HandlerObjects);
  1427. await _workflowRepository.UpdateAsync(workflow, cancellationToken);
  1428. //calc workflow expired time
  1429. var isOrgToCenter = CheckIfFlowFromOrgToCenter(workflow, targetStep);
  1430. await _publisher.PublishAsync(new RecallNotify(workflow, targetStep, dto, isOrgToCenter),
  1431. PublishStrategy.ParallelWhenAll, cancellationToken);
  1432. return isOrgToCenter;
  1433. }
  1434. /// <summary>
  1435. /// 撤回至开始节点
  1436. /// </summary>
  1437. public async Task RecallToStartStepAsync(Workflow workflow, string opinion, ReverseFlowStepAssignInfo reverseFlowStepAssignInfo,
  1438. DateTime? expiredTime, bool isOrderFiled, EHandleMode handleMode, CancellationToken cancellationToken)
  1439. {
  1440. //todo 1.当前待办节点删掉 2.当前待办trace更新(status, opinion) 3.复制startStep为待办 4.更新workflow(status, csStatus, handlers) 5.publish event
  1441. var startStep = workflow.Steps.Where(d => d.StepType == EStepType.Start && d.IsOrigin)
  1442. .MaxBy(d => d.CreationTime);
  1443. if (startStep is null)
  1444. throw new UserFriendlyException($"数据异常, workflowId: {workflow.Id}", "该流程无开始节点");
  1445. //await RecallToTargetStepAsync(workflow, startStep, opinion, current, cancellationToken);
  1446. var startStepDefine = workflow.WorkflowDefinition.Steps.FirstOrDefault(d => d.StepType == EStepType.Start);
  1447. var dto = new RecallDto
  1448. {
  1449. Opinion = opinion,
  1450. NextStepCode = startStep.Code,
  1451. NextStepName = startStep.Name,
  1452. BusinessType = startStep.BusinessType,
  1453. StepType = startStep.StepType,
  1454. HandlerType = startStepDefine.HandlerType,
  1455. NextHandlers = new List<FlowStepHandler>
  1456. {
  1457. new FlowStepHandler
  1458. {
  1459. Key = startStep.RoleId,
  1460. Value = startStep.RoleName,
  1461. RoleId = startStep.RoleId,
  1462. RoleName = startStep.RoleName,
  1463. UserId = startStep.HandlerId,
  1464. Username = startStep.HandlerName,
  1465. OrgId = startStep.HandlerOrgId,
  1466. OrgName = startStep.HandlerOrgName,
  1467. }
  1468. }
  1469. };
  1470. //var flowAssignInfo = await GetNextStepFlowAssignInfoByDefineAsync(targetStepDefine, dto.HandlerType, dto.IsStartCountersign,
  1471. // dto.NextHandlers.Select(d => new Kv(d.Key, d.Value)).ToList(), cancellationToken);
  1472. ////flowAssignInfo.FlowAssignType = EFlowAssignType.Role;
  1473. //await RecallAsync(workflow, dto, targetStepDefine, flowAssignInfo, EWorkflowTraceType.Recall, expiredTime, isOrderFiled,
  1474. // handleMode, cancellationToken);
  1475. await RecallAsync(workflow, dto, reverseFlowStepAssignInfo, startStepDefine, startStep,
  1476. EWorkflowTraceType.Recall, expiredTime, isOrderFiled,
  1477. handleMode, cancellationToken: cancellationToken);
  1478. }
  1479. /// <summary>
  1480. /// 撤回至派单节点
  1481. /// </summary>
  1482. public async Task RecallToSendStepAsync(Workflow workflow, string opinion, ReverseFlowStepAssignInfo reverseFlowStepAssignInfo,
  1483. DateTime? expiredTime, bool isOrderFiled, EHandleMode handleMode, CancellationToken cancellationToken)
  1484. {
  1485. var sendStep = workflow.Steps.Where(d => d.BusinessType == EBusinessType.Send && d.IsOrigin)
  1486. .MaxBy(d => d.CreationTime);
  1487. if (sendStep is null)
  1488. throw new UserFriendlyException($"未找到派单节点, workflowId: {workflow.Id}", "该流程无派单节点");
  1489. //await RecallToTargetStepAsync(workflow, sendStep, opinion, current, cancellationToken);
  1490. var sendStepDefine = workflow.WorkflowDefinition.Steps.FirstOrDefault(d => d.BusinessType == EBusinessType.Send);
  1491. var dto = new RecallDto
  1492. {
  1493. Opinion = opinion,
  1494. NextStepCode = sendStep.Code,
  1495. NextStepName = sendStep.Name,
  1496. BusinessType = sendStep.BusinessType,
  1497. StepType = sendStep.StepType,
  1498. HandlerType = sendStepDefine.HandlerType,
  1499. NextHandlers = new List<FlowStepHandler>
  1500. {
  1501. new FlowStepHandler
  1502. {
  1503. Key = sendStep.RoleId,
  1504. Value = sendStep.RoleName,
  1505. RoleId = sendStep.RoleId,
  1506. RoleName = sendStep.RoleName,
  1507. UserId = sendStep.HandlerId,
  1508. Username = sendStep.HandlerName,
  1509. OrgId = sendStep.HandlerOrgId,
  1510. OrgName = sendStep.HandlerOrgName,
  1511. }
  1512. }
  1513. };
  1514. //var flowAssignInfo = await GetNextStepFlowAssignInfoByDefineAsync(targetStepDefine, dto.HandlerType, dto.IsStartCountersign,
  1515. // dto.NextHandlers.Select(d => new Kv(d.Key, d.Value)).ToList(), cancellationToken);
  1516. ////flowAssignInfo.FlowAssignType = EFlowAssignType.Role;
  1517. //await RecallAsync(workflow, dto, targetStepDefine, flowAssignInfo, EWorkflowTraceType.Recall, expiredTime, isOrderFiled,
  1518. // handleMode, cancellationToken);
  1519. await RecallAsync(workflow, dto, reverseFlowStepAssignInfo, sendStepDefine, sendStep,
  1520. EWorkflowTraceType.Recall, expiredTime, isOrderFiled,
  1521. handleMode, cancellationToken: cancellationToken);
  1522. }
  1523. /// <summary>
  1524. /// 特提至中心(优先派单组其次坐席)
  1525. /// </summary>
  1526. /// <returns>true 派单组 false 话务部</returns>
  1527. public async Task<(bool, Workflow workflow)> RecallToCenterFirstToSendAsync(string workflowId, string opinion,
  1528. ReverseFlowStepAssignInfo reverseFlowStepAssignInfo,
  1529. bool isOrderFiled, DateTime? expiredTime, EHandleMode handleMode, CancellationToken cancellationToken)
  1530. {
  1531. var isPaiDan = false;
  1532. var workflow = await GetWorkflowAsync(workflowId, withDefine: true, withSteps: true, withTraces: true, withCountersigns: true,
  1533. cancellationToken: cancellationToken);
  1534. var hasSendStep = workflow.Steps.Any(d => d.BusinessType == EBusinessType.Send);
  1535. if (hasSendStep)
  1536. {
  1537. //var targetStepDefine = workflow.WorkflowDefinition.Steps.FirstOrDefault(d => d.BusinessType == EBusinessType.Send);
  1538. //var dto = new RecallDto
  1539. //{
  1540. // Opinion = opinion,
  1541. // NextStepCode = sendStep.Code,
  1542. // NextStepName = sendStep.Name,
  1543. // BusinessType = sendStep.BusinessType,
  1544. // StepType = sendStep.StepType,
  1545. // HandlerType = targetStepDefine.HandlerType,
  1546. // NextHandlers = new List<FlowStepHandler>
  1547. // {
  1548. // new FlowStepHandler
  1549. // {
  1550. // Key = sendStep.RoleId,
  1551. // Value = sendStep.RoleName,
  1552. // RoleId = sendStep.RoleId,
  1553. // RoleName = sendStep.RoleName,
  1554. // UserId = sendStep.HandlerId,
  1555. // Username = sendStep.HandlerName,
  1556. // OrgId = sendStep.HandlerOrgId,
  1557. // OrgName = sendStep.HandlerOrgName,
  1558. // }
  1559. // }
  1560. //};
  1561. //if (_appOptions.Value.IsZiGong && handlers != null && handlers.Any())
  1562. //{
  1563. // dto.NextHandlers = handlers;
  1564. //}
  1565. //var flowAssignInfo = await GetNextStepFlowAssignInfoByDefineAsync(targetStepDefine, dto.HandlerType, dto.IsStartCountersign,
  1566. // dto.NextHandlers.Select(d => new Kv(d.Key, d.Value)).ToList(), cancellationToken);
  1567. //await RecallAsync(workflow, dto, targetStepDefine, flowAssignInfo, EWorkflowTraceType.Recall, expiredTime, isOrderFiled,
  1568. // handleMode, cancellationToken);
  1569. await RecallToSendStepAsync(workflow, opinion, reverseFlowStepAssignInfo, expiredTime, isOrderFiled,
  1570. handleMode, cancellationToken);
  1571. isPaiDan = true;
  1572. }
  1573. else
  1574. {
  1575. //var startStep = workflow.Steps.First(d => d.StepType == EStepType.Start);
  1576. //if (startStep is null)
  1577. // throw new UserFriendlyException($"数据异常, workflowId: {workflowId}", "该流程无开始节点");
  1578. ////await RecallToTargetStepAsync(workflow, startStep, opinion, current, cancellationToken);
  1579. //var targetStepDefine = workflow.WorkflowDefinition.Steps.FirstOrDefault(d => d.StepType == EStepType.Start);
  1580. //var dto = new RecallDto
  1581. //{
  1582. // Opinion = opinion,
  1583. // NextStepCode = startStep.Code,
  1584. // NextStepName = startStep.Name,
  1585. // BusinessType = startStep.BusinessType,
  1586. // StepType = startStep.StepType,
  1587. // HandlerType = targetStepDefine.HandlerType,
  1588. // NextHandlers = new List<FlowStepHandler>
  1589. // {
  1590. // new FlowStepHandler
  1591. // {
  1592. // Key = startStep.RoleId,
  1593. // Value = startStep.RoleName,
  1594. // RoleId = startStep.RoleId,
  1595. // RoleName = startStep.RoleName,
  1596. // UserId = startStep.HandlerId,
  1597. // Username = startStep.HandlerName,
  1598. // OrgId = startStep.HandlerOrgId,
  1599. // OrgName = startStep.HandlerOrgName,
  1600. // }
  1601. // }
  1602. //};
  1603. //var flowAssignInfo = await GetNextStepFlowAssignInfoByDefineAsync(targetStepDefine, dto.HandlerType, dto.IsStartCountersign,
  1604. // dto.NextHandlers.Select(d => new Kv(d.Key, d.Value)).ToList(), cancellationToken);
  1605. ////flowAssignInfo.FlowAssignType = EFlowAssignType.Role;
  1606. //await RecallAsync(workflow, dto, targetStepDefine, flowAssignInfo, EWorkflowTraceType.Recall, expiredTime, isOrderFiled,
  1607. // handleMode, cancellationToken);
  1608. await RecallToStartStepAsync(workflow, opinion, reverseFlowStepAssignInfo, expiredTime, isOrderFiled,
  1609. handleMode, cancellationToken);
  1610. //isPaiDan = false;
  1611. }
  1612. return (new(isPaiDan, workflow));
  1613. }
  1614. //private async Task RecallToTargetStepAsync(Workflow workflow, WorkflowStep targetStep, string opinion, ISessionContext current,
  1615. // CancellationToken cancellationToken)
  1616. //{
  1617. // //update uncompleted traces
  1618. // await RecallTraceAsync(workflow.Traces, opinion, current, cancellationToken);
  1619. // await _workflowStepRepository.RemoveRangeAsync(workflow.Steps, cancellationToken);
  1620. // workflow.Steps.RemoveAll(_ => true);
  1621. // workflow.EndCountersign();
  1622. // workflow.ResetOption();
  1623. // if (workflow.Status is EWorkflowStatus.Completed)
  1624. // workflow.SetStatusRunnable();
  1625. // var newStartStep =
  1626. // await DuplicateStepWithTraceAsync(workflow, targetStep, EWorkflowTraceType.Recall, cancellationToken);
  1627. // workflow.UpdateActualStepWhenAssign(targetStep, new FlowStepHandler
  1628. // {
  1629. // UserId = targetStep.HandlerId,
  1630. // Username = targetStep.HandlerName,
  1631. // OrgId = targetStep.HandlerOrgId,
  1632. // OrgName = targetStep.HandlerOrgName,
  1633. // });
  1634. // workflow.UpdateCurrentStepWhenAssign(targetStep, new FlowStepHandler
  1635. // {
  1636. // UserId = targetStep.HandlerId,
  1637. // Username = targetStep.HandlerName,
  1638. // OrgId = targetStep.HandlerOrgId,
  1639. // OrgName = targetStep.HandlerOrgName,
  1640. // });
  1641. // var isOrgToCenter = CheckIfFlowFromOrgToCenter(workflow, targetStep);
  1642. // var flowAssignInfo = FlowAssignInfo.Create(targetStep.FlowAssignType.Value, targetStep.Handlers);
  1643. // workflow.ResetHandlers(flowAssignInfo.FlowAssignType, flowAssignInfo.HandlerObjects);
  1644. // await _workflowRepository.UpdateAsync(workflow, cancellationToken);
  1645. // var dto = _mapper.Map<RecallDto>(targetStep);
  1646. // dto.WorkflowId = workflow.Id;
  1647. // await _publisher.PublishAsync(new RecallNotify(workflow, targetStep, dto, isOrgToCenter),
  1648. // PublishStrategy.ParallelWhenAll, cancellationToken);
  1649. //}
  1650. /// <summary>
  1651. /// 补充
  1652. /// </summary>
  1653. /// <returns></returns>
  1654. public async Task SupplementAsync(Workflow workflow, EndWorkflowDto dto, CancellationToken cancellationToken)
  1655. {
  1656. CheckWhetherRunnable(workflow.Status);
  1657. //todo 检查当前办理人是否为该流程中的办理人
  1658. var supplement = _mapper.Map<WorkflowSupplement>(dto);
  1659. //await _workflowSupplementRepository.AddAsync(supplement, cancellationToken);
  1660. }
  1661. /// <summary>
  1662. /// 终止流程
  1663. /// </summary>
  1664. public async Task TerminateAsync(TerminateDto dto, CancellationToken cancellationToken)
  1665. {
  1666. var workflow = await _workflowRepository.GetAsync(dto.WorkflowId, cancellationToken);
  1667. if (workflow == null)
  1668. throw UserFriendlyException.SameMessage("无效的流程编号");
  1669. workflow.Terminate(dto.Opinion);
  1670. await _workflowRepository.UpdateAsync(workflow, cancellationToken);
  1671. await _publisher.PublishAsync(new TerminalWorkflowNotify(workflow), PublishStrategy.ParallelWhenAll,
  1672. cancellationToken);
  1673. }
  1674. /// <summary>
  1675. /// 根据stepCode查询流程配置中对应的节点
  1676. /// </summary>
  1677. public StepDefine GetStepDefine(WorkflowDefinition workflowDefinition, string stepCode)
  1678. {
  1679. if (workflowDefinition == null) throw new ArgumentNullException(nameof(workflowDefinition));
  1680. if (string.IsNullOrEmpty(stepCode)) throw new ArgumentNullException(nameof(stepCode));
  1681. var stepDefine = workflowDefinition.FindStepDefine(stepCode);
  1682. if (stepDefine == null)
  1683. throw new UserFriendlyException(
  1684. $"未找到流程中对应的节点,DefineCode: {workflowDefinition.Code}, stepCode: {stepCode}",
  1685. "未查询到对应节点");
  1686. return stepDefine;
  1687. }
  1688. /// <summary>
  1689. /// 查询当前待办理节点
  1690. /// </summary>
  1691. public WorkflowStep FindCurrentStepWaitForHandle(Workflow workflow, string userId, string orgId,
  1692. string[] roleIds) =>
  1693. GetUnHandleStep(workflow.Steps, orgId, userId, roleIds);
  1694. /// <summary>
  1695. /// 查询当前节点中最后一个节点
  1696. /// </summary>
  1697. public async Task<WorkflowStep?> FindLastStepAsync(string workflowId, CancellationToken cancellationToken)
  1698. {
  1699. var workflow = await GetWorkflowAsync(workflowId, withSteps: true, cancellationToken: cancellationToken);
  1700. return workflow.Steps.MaxBy(d => d.CreationTime);
  1701. }
  1702. /// <summary>
  1703. /// 查询所有办理部门及实际办理部门
  1704. /// </summary>
  1705. /// <returns></returns>
  1706. public async Task<(Kv, IReadOnlyList<Kv>)> GetHandleOrgsAsync(string workflowId,
  1707. CancellationToken cancellationToken)
  1708. {
  1709. var workflow = await GetWorkflowAsync(workflowId, withTraces: true, cancellationToken: cancellationToken);
  1710. var steps = workflow.Traces
  1711. .Where(d => d.StepType is EStepType.Normal)
  1712. .ToList();
  1713. var items = steps.Where(d => d.TraceType == EWorkflowTraceType.Normal || d.TraceType == EWorkflowTraceType.Jump)
  1714. .Select(d => new Kv(d.HandlerOrgId, d.HandlerOrgName))
  1715. .DistinctBy(d => d.Key).ToList();
  1716. return (new Kv(workflow.ActualHandleOrgCode, workflow.ActualHandleOrgName), items);
  1717. }
  1718. /// <summary>
  1719. /// 新增流程流转记录
  1720. /// </summary>
  1721. public async Task AddStepsAsync(string workflowId, List<WorkflowStep> steps, CancellationToken cancellationToken)
  1722. {
  1723. var workflow = await GetWorkflowAsync(workflowId, cancellationToken: cancellationToken);
  1724. if (workflow is null)
  1725. throw new UserFriendlyException("找不到该流程");
  1726. await _workflowStepRepository.AddNav(steps)
  1727. .Include(d => d.WorkflowTrace)
  1728. .ExecuteCommandAsync();
  1729. }
  1730. /// <summary>
  1731. /// 创建开始节点
  1732. /// </summary>
  1733. public WorkflowStep CreateStartStep(Workflow workflow, StepDefine startStepDefine,
  1734. BasicWorkflowDto dto, UserInfo assigner, FlowStepHandler handler, DateTime? expiredTime,
  1735. EFlowAssignType? flowAssignType = EFlowAssignType.User)
  1736. {
  1737. //startstep
  1738. var nextSteps = _mapper.Map<List<StepSimple>>(startStepDefine.NextSteps);
  1739. if (startStepDefine.InstanceMode is EInstanceMode.Config)
  1740. {
  1741. var selectedStep = nextSteps.FirstOrDefault(d => d.Code == dto.NextStepCode);
  1742. if (selectedStep is not null)
  1743. selectedStep.Selected = true;
  1744. }
  1745. var startStep = _mapper.Map<WorkflowStep>(startStepDefine);
  1746. _mapper.Map(workflow, startStep);
  1747. startStep.FlowAssignType = flowAssignType;
  1748. startStep.Handlers = new List<Kv> { new(handler.Key, handler.Value) };
  1749. startStep.NextSteps = nextSteps;
  1750. startStep.IsMain = true;
  1751. startStep.IsOrigin = true;
  1752. startStep.Status = EWorkflowStepStatus.WaitForAccept;
  1753. startStep.FlowDirection = dto.FlowDirection;
  1754. startStep.PrevChosenStepCode = null;
  1755. if (expiredTime.HasValue)
  1756. startStep.StepExpiredTime = expiredTime;
  1757. startStep.Assign(handler.UserId, handler.Username,
  1758. handler.OrgId, handler.OrgName,
  1759. handler.RoleId, handler.RoleName);
  1760. startStep.AssignerId = assigner.UserId;
  1761. startStep.AssignerName = assigner.UserName;
  1762. startStep.AssignerOrgId = assigner.OrgId;
  1763. startStep.AssignerOrgName = assigner.OrgName;
  1764. startStep.AssignerOrgIsCenter = assigner.OrgIsCenter;
  1765. startStep.InitId();
  1766. return startStep;
  1767. }
  1768. /// <summary>
  1769. /// 流程结束
  1770. /// </summary>
  1771. public async Task<WorkflowTrace> EndAsync(ISessionContext current, Workflow workflow, BasicWorkflowDto dto,
  1772. StepDefine endStepDefine, WorkflowStep currentStep,
  1773. DateTime? expiredTime, CancellationToken cancellationToken)
  1774. {
  1775. //create endStep
  1776. var endStep = await CreateEndStepAsync(workflow, endStepDefine, currentStep, dto, expiredTime,
  1777. cancellationToken);
  1778. //workflow.Steps.Add(endStep);
  1779. //update endTrace
  1780. var endTrace = await NextTraceAsync(workflow, dto, endStep, cancellationToken);
  1781. //create publish trace
  1782. if (workflow.FlowType is EFlowType.Handle)
  1783. await CreatePublishTraceAsync(endTrace, cancellationToken);
  1784. workflow.Complete(endStep, dto.ReviewResult);
  1785. //需求调整:归档时当前节点显示为归档节点
  1786. workflow.UpdateCurrentStepWhenHandle(endStep, current.OrgAreaCode, current.OrgAreaName, current.OrgLevel);
  1787. workflow.CurrentStepAcceptTime = endStep.AcceptTime;
  1788. // workflow.UpdateActualStepWhenHandle(endStep, current.OrgAreaCode, current.OrgAreaName, current.OrgLevel);
  1789. // workflow.ActualHandleStepAcceptTime = endStep.AcceptTime.Value;
  1790. if (string.IsNullOrEmpty(workflow.OrgLevelOneCode))
  1791. workflow.UpdateLevelOneOrg(workflow.ActualHandleOrgCode, workflow.ActualHandleOrgName);
  1792. await _workflowRepository.UpdateAsync(workflow, cancellationToken);
  1793. await _publisher.PublishAsync(new EndWorkflowNotify(workflow, endTrace, dto),
  1794. PublishStrategy.ParallelWhenAll, cancellationToken);
  1795. return endTrace;
  1796. }
  1797. ///// <summary>
  1798. ///// 判断会签类型(中心会签或部门会签)
  1799. ///// </summary>
  1800. ///// <param name="businessType"></param>
  1801. ///// <returns></returns>
  1802. ///// <exception cref="ArgumentOutOfRangeException"></exception>
  1803. //public ECounterSignType? GetCounterSignType(EBusinessType businessType) =>
  1804. // businessType switch
  1805. // {
  1806. // EBusinessType.Seat => ECounterSignType.Seat,
  1807. // EBusinessType.Send => ECounterSignType.Seat,
  1808. // EBusinessType.Department => ECounterSignType.Department,
  1809. // EBusinessType.File => null,
  1810. // _ => throw new ArgumentOutOfRangeException(nameof(businessType), businessType, null)
  1811. // };
  1812. public ECounterSignType? GetCounterSignType(bool isStartCountersign, EBusinessType currentStepBusinessType)
  1813. {
  1814. if (!isStartCountersign) return null;
  1815. return currentStepBusinessType switch
  1816. {
  1817. EBusinessType.Seat => ECounterSignType.Center,
  1818. EBusinessType.Send => ECounterSignType.Center,
  1819. EBusinessType.Department => ECounterSignType.Department,
  1820. EBusinessType.DepartmentLeader => ECounterSignType.Department,
  1821. EBusinessType.File => null,
  1822. _ => throw new ArgumentOutOfRangeException(nameof(currentStepBusinessType), currentStepBusinessType, null)
  1823. };
  1824. // return _sessionContextProvider.SessionContext.OrgIsCenter ? ECounterSignType.Center : ECounterSignType.Department;
  1825. }
  1826. /// <summary>
  1827. /// 办理节点
  1828. /// </summary>
  1829. public async Task HandleStepAsync(WorkflowStep step, Workflow workflow,
  1830. BasicWorkflowDto dto, ECounterSignType? counterSignType,
  1831. DateTime? expiredTime, EHandleMode handleMode, CancellationToken cancellationToken)
  1832. {
  1833. if (step.Status is EWorkflowStepStatus.Handled)
  1834. throw UserFriendlyException.SameMessage("当前节点状态已办理");
  1835. if (step.StepType is EStepType.End)
  1836. throw new UserFriendlyException("当前流程已流转到最终步骤");
  1837. if (dto.IsStartCountersign && !counterSignType.HasValue)
  1838. throw new UserFriendlyException("缺少会签类型参数");
  1839. //办理参数
  1840. //_mapper.Map(dto, step);
  1841. step.NextHandlers = dto.NextHandlers;
  1842. step.NextMainHandler = dto.NextMainHandler;
  1843. step.NextStepCode = dto.NextStepCode;
  1844. step.IsSms = dto.IsSms;
  1845. step.Opinion = dto.Opinion;
  1846. step.Remark = dto.Remark;
  1847. step.ReviewResult = dto.ReviewResult;
  1848. if (workflow.FlowType is EFlowType.Review)
  1849. step.HandleMode = dto.ReviewResult == EReviewResult.Approval
  1850. ? EHandleMode.Approved
  1851. : EHandleMode.NotApproved;
  1852. //step办理状态
  1853. HandleStep(step, handleMode, dto.Opinion, dto.NextStepCode);
  1854. }
  1855. /// <summary>
  1856. /// 查找会签循环的初始会签发起节点
  1857. /// </summary>
  1858. /// <param name="steps"></param>
  1859. /// <param name="currentStep"></param>
  1860. /// <returns></returns>
  1861. /// <exception cref="UserFriendlyException"></exception>
  1862. public WorkflowStep GetCsLoopStartStep(List<WorkflowStep> steps, WorkflowStep currentStep)
  1863. {
  1864. var startCountersignStep = steps.FirstOrDefault(d => d.Id == currentStep.CountersignStartStepId);
  1865. if (startCountersignStep is null)
  1866. throw new UserFriendlyException(
  1867. $"GetCsLoopStartStep: 未查询到会签开始节点,workflowId: {currentStep.WorkflowId}, startStepId: {currentStep.CountersignStartStepId}",
  1868. "未查询到会签开始节点,数据异常");
  1869. if (!startCountersignStep.IsCountersignEndStep)
  1870. return startCountersignStep;
  1871. return GetCsLoopStartStep(steps, startCountersignStep);
  1872. }
  1873. public async Task HandlePublishTraceAsync(string workflowId, string orderPublishId,
  1874. UserInfo acceptor, UserInfo handler, DateTime handleTime, UserInfo visitAcceptor, string orderVisitId, CancellationToken cancellation)
  1875. {
  1876. if (string.IsNullOrEmpty(orderPublishId))
  1877. throw new UserFriendlyException($"参数异常,orderPublishId不能为空, workflowId: {workflowId}");
  1878. //handle pubtrace
  1879. var pubTrace = await _workflowTraceRepository.Queryable()
  1880. .FirstAsync(d => d.WorkflowId == workflowId
  1881. && d.TraceStyle == ETraceStyle.Publish
  1882. && d.Status == EWorkflowStepStatus.WaitForAccept, cancellation);
  1883. if (pubTrace is not null)
  1884. {
  1885. pubTrace.OrderPublishId = orderPublishId;
  1886. pubTrace.AcceptorId = acceptor.UserId;
  1887. pubTrace.AcceptorName = acceptor.UserName;
  1888. pubTrace.AcceptorOrgId = acceptor.OrgId;
  1889. pubTrace.AcceptorOrgName = acceptor.OrgName;
  1890. pubTrace.AcceptTime = handleTime;
  1891. pubTrace.HandlerId = handler.UserId;
  1892. pubTrace.HandlerName = handler.UserName;
  1893. pubTrace.HandlerOrgId = handler.OrgId;
  1894. pubTrace.HandlerOrgName = handler.OrgName;
  1895. pubTrace.HandleTime = handleTime;
  1896. pubTrace.Status = EWorkflowStepStatus.Handled;
  1897. await _workflowTraceRepository.UpdateAsync(pubTrace, cancellation);
  1898. //create visit trace
  1899. await CreateVisitTraceAsync(pubTrace, visitAcceptor, orderVisitId, cancellation);
  1900. }
  1901. else
  1902. {
  1903. //throw new UserFriendlyException($"未查询到待办的发布节点, workflowId:{workflowId}");
  1904. }
  1905. }
  1906. public async Task HandleVisitTraceAsync(string orderVisitId, UserInfo visitor, DateTime visitTime, CancellationToken cancellation)
  1907. {
  1908. var visitTrace = await _workflowTraceRepository.GetAsync(d => d.OrderVisitId == orderVisitId,
  1909. cancellationToken: cancellation);
  1910. if (visitTrace is not null)
  1911. {
  1912. visitTrace.AcceptorId = visitor.UserId;
  1913. visitTrace.AcceptorName = visitor.UserName;
  1914. visitTrace.AcceptorOrgId = visitor.OrgId;
  1915. visitTrace.AcceptorOrgName = visitor.OrgName;
  1916. visitTrace.HandlerId = visitor.UserId;
  1917. visitTrace.HandlerName = visitor.UserName;
  1918. visitTrace.HandlerOrgId = visitor.OrgId;
  1919. visitTrace.HandlerOrgName = visitor.OrgName;
  1920. visitTrace.HandleTime = visitTime;
  1921. visitTrace.Status = EWorkflowStepStatus.Handled;
  1922. await _workflowTraceRepository.UpdateAsync(visitTrace, cancellation);
  1923. //create append end trace
  1924. await CreateTrashEndTraceAsync(visitTrace, cancellation);
  1925. }
  1926. else
  1927. {
  1928. //throw new UserFriendlyException($"未查询到待办的发布节点, orderVisitId:{orderVisitId}");
  1929. }
  1930. }
  1931. #region private method
  1932. private StepAssignInfo GetStepAssignInfo(ReverseFlowStepAssignInfo assignInfo,
  1933. WorkflowStep? targetStep = null, StepDefine? targetStepDefine = null)
  1934. {
  1935. switch (assignInfo.ReverseFlowStepCreationPolicy)
  1936. {
  1937. case EReverseFlowStepCreationPolicy.OriginStep:
  1938. if (targetStep is null)
  1939. throw new UserFriendlyException("参数异常:原节点信息为空");
  1940. return GetStepAssignInfo(targetStep);
  1941. case EReverseFlowStepCreationPolicy.OriginStepUser:
  1942. if (string.IsNullOrEmpty(targetStep?.HandlerId))
  1943. throw new UserFriendlyException("参数异常:原节点办理人为空");
  1944. return GetStepAssignInfo(targetStep, EFlowAssignType.User);
  1945. case EReverseFlowStepCreationPolicy.OriginStepOrg:
  1946. if (string.IsNullOrEmpty(targetStep?.HandlerOrgId))
  1947. throw new UserFriendlyException("参数异常:原节点办理部门为空");
  1948. return GetStepAssignInfo(targetStep, EFlowAssignType.Org);
  1949. case EReverseFlowStepCreationPolicy.OriginStepRole:
  1950. if (string.IsNullOrEmpty(targetStep?.RoleId))
  1951. throw new UserFriendlyException("参数异常:原节点办理角色为空");
  1952. return GetStepAssignInfo(targetStep, EFlowAssignType.Role);
  1953. case EReverseFlowStepCreationPolicy.OriginStepOrgAndRole:
  1954. if (string.IsNullOrEmpty(targetStep?.RoleId) || string.IsNullOrEmpty(targetStep?.HandlerOrgId))
  1955. throw new UserFriendlyException("参数异常:原节点办理角色或部门为空");
  1956. return GetStepAssignInfo(targetStep, EFlowAssignType.OrgAndRole);
  1957. case EReverseFlowStepCreationPolicy.AssignHandler:
  1958. if (assignInfo?.StepAssignInfo is null)
  1959. throw new UserFriendlyException("参数异常:节点指定办理对象信息为空");
  1960. return assignInfo.StepAssignInfo;
  1961. case EReverseFlowStepCreationPolicy.OriginDefinition:
  1962. if (targetStepDefine is null)
  1963. throw new UserFriendlyException("参数异常:节点配置信息为空");
  1964. return GetStepAssignInfo(targetStepDefine);
  1965. default:
  1966. throw new ArgumentOutOfRangeException();
  1967. }
  1968. }
  1969. private StepAssignInfo GetStepAssignInfo(StepDefine stepDefine)
  1970. {
  1971. var handler = stepDefine.HandlerTypeItems.FirstOrDefault();
  1972. if (handler is null)
  1973. throw new UserFriendlyException($"未正确配置节点办理对象, stepcode: {stepDefine.Code}", "未正确配置节点办理对象");
  1974. var rsp = new StepAssignInfo();
  1975. switch (stepDefine.HandlerType)
  1976. {
  1977. case EHandlerType.Role:
  1978. rsp.FlowAssignType = EFlowAssignType.Role;
  1979. rsp.RoleId = handler.Key;
  1980. rsp.RoleName = handler.Value;
  1981. break;
  1982. case EHandlerType.AssignedUser:
  1983. rsp.FlowAssignType = EFlowAssignType.User;
  1984. rsp.UserId = handler.Key;
  1985. rsp.Username = handler.Value;
  1986. break;
  1987. case EHandlerType.AssignedOrg:
  1988. rsp.FlowAssignType = EFlowAssignType.Org;
  1989. rsp.OrgId = handler.Key;
  1990. rsp.OrgName = handler.Value;
  1991. break;
  1992. case EHandlerType.OrgType:
  1993. throw new ArgumentOutOfRangeException("部门类型不支持按配置指派");
  1994. case EHandlerType.OrgLevel:
  1995. throw new ArgumentOutOfRangeException("部门等级不支持按配置指派");
  1996. default:
  1997. throw new ArgumentOutOfRangeException();
  1998. }
  1999. return rsp;
  2000. }
  2001. private StepAssignInfo GetStepAssignInfo(WorkflowStep targetStep, EFlowAssignType? flowAssignType = null)
  2002. {
  2003. return new StepAssignInfo
  2004. {
  2005. FlowAssignType = (flowAssignType ?? targetStep.FlowAssignType) ?? EFlowAssignType.User,
  2006. UserId = targetStep.HandlerId,
  2007. Username = targetStep.HandlerName,
  2008. OrgId = targetStep.HandlerOrgId,
  2009. OrgName = targetStep.HandlerOrgName,
  2010. RoleId = targetStep.RoleId,
  2011. RoleName = targetStep.RoleName,
  2012. };
  2013. }
  2014. private Workflow CreateWorkflow(WorkflowModule wfModule, string title, string? externalId = null)
  2015. {
  2016. var definition = wfModule.Definition;
  2017. if (definition is null)
  2018. throw new UserFriendlyException("无效流程模板");
  2019. var workflow = new Workflow
  2020. {
  2021. Title = title,
  2022. ModuleId = wfModule.Id,
  2023. ModuleName = wfModule.Name,
  2024. ModuleCode = wfModule.Code,
  2025. DefinitionId = definition.Id,
  2026. Status = EWorkflowStatus.Runnable,
  2027. Steps = new(),
  2028. Traces = new(),
  2029. WorkflowDefinition = definition,
  2030. ExternalId = externalId ?? string.Empty,
  2031. FlowType = definition.FlowType,
  2032. };
  2033. workflow.InitId();
  2034. return workflow;
  2035. }
  2036. private static void UpdateCurrentStep(Workflow workflow, BasicWorkflowDto dto,
  2037. StepDefine nextStepDefine, List<WorkflowStep> nextSteps)
  2038. {
  2039. if (dto.IsStartCountersign) return;
  2040. if (workflow.IsInCountersign) return;
  2041. if (nextStepDefine.BusinessType is EBusinessType.Seat or EBusinessType.Send)
  2042. {
  2043. //坐席->派单不选办理对象时
  2044. workflow.UpdateCurrentStepWhenAssign(nextSteps.First(),
  2045. new FlowStepHandler
  2046. {
  2047. OrgId = OrgSeedData.CenterId,
  2048. OrgName = OrgSeedData.CenterName
  2049. });
  2050. }
  2051. else
  2052. {
  2053. var nextHandler = dto.NextHandlers.First();
  2054. workflow.UpdateCurrentStepWhenAssign(nextSteps.First(), nextHandler);
  2055. }
  2056. }
  2057. private static void UpdateActualStep(Workflow workflow, BasicWorkflowDto dto,
  2058. StepDefine nextStepDefine, List<WorkflowStep> nextSteps)
  2059. {
  2060. if (dto.IsStartCountersign) return;
  2061. if (workflow.IsInCountersign) return;
  2062. if (nextStepDefine.StepType is EStepType.Summary or EStepType.End) return;
  2063. if (nextSteps.Count > 1) return; //多个下级节点不更新workflow的实际办理信息
  2064. var nextStep = nextSteps.First();
  2065. //todo 重构为办理对象由参数传入,指派给中心?派单员?待确认
  2066. if (nextStepDefine.BusinessType is EBusinessType.Seat or EBusinessType.Send)
  2067. {
  2068. //坐席->派单不选办理对象时
  2069. workflow.UpdateActualStepWhenAssign(nextStep,
  2070. new FlowStepHandler
  2071. {
  2072. Key = OrgSeedData.CenterId,
  2073. Value = OrgSeedData.CenterName,
  2074. OrgId = OrgSeedData.CenterId,
  2075. OrgName = OrgSeedData.CenterName
  2076. });
  2077. }
  2078. else
  2079. {
  2080. var nextHandler = dto.NextHandlers.First();
  2081. workflow.UpdateActualStepWhenAssign(nextStep, nextHandler);
  2082. }
  2083. //与实际办理节点的接办状态保持一致
  2084. workflow.ActualHandleStepAcceptTime = nextStep.AcceptTime;
  2085. }
  2086. private async Task<WorkflowStep> CreateStartStepAsync(Workflow workflow, StepDefine startStepDefine,
  2087. BasicWorkflowDto dto, UserInfo assigner, FlowStepHandler handler, EWorkflowTraceType traceType,
  2088. DateTime? expiredTime, EFlowAssignType? flowAssignType, CancellationToken cancellationToken)
  2089. {
  2090. var startStep = CreateStartStep(workflow, startStepDefine, dto, assigner, handler, expiredTime, flowAssignType);
  2091. await _workflowStepRepository.AddAsync(startStep, cancellationToken);
  2092. await CreateTraceAsync(workflow, startStep, traceType, cancellationToken);
  2093. return startStep;
  2094. }
  2095. /// <summary>
  2096. /// 创建下1/N个节点
  2097. /// </summary>
  2098. private async Task<List<WorkflowStep>> CreateNextStepsAsync(Workflow workflow, WorkflowStep currentStep,
  2099. BasicWorkflowDto dto, StepDefine nextStepDefine, UserInfo assigner, bool isNextDynamic,
  2100. DateTime? expiredTime, bool isStartCountersign, bool isAutoFillSummaryOpinion = false,
  2101. Action<WorkflowStep>? stepConfig = null, CancellationToken cancellationToken = default)
  2102. {
  2103. List<WorkflowStep> nextSteps = new();
  2104. if (currentStep.IsInCountersign())
  2105. {
  2106. if (currentStep.IsCountersignEndStep)
  2107. {
  2108. // check if current is topend f: csStartStep.prev
  2109. // t: check if dto.StartCs t: csconfig f: config
  2110. if (currentStep.IsTopCountersignEndStep(workflow.TopCountersignStepId))
  2111. {
  2112. if (isStartCountersign)
  2113. {
  2114. //依据会签策略创建会签下一级节点
  2115. nextSteps = await CreateCountersignStepsAsync(workflow, nextStepDefine, currentStep, dto,
  2116. assigner, expiredTime, stepConfig, cancellationToken);
  2117. }
  2118. else
  2119. {
  2120. //创建普通节点(根据配置)
  2121. nextSteps = await CreateConfigStepsAsync(workflow, nextStepDefine, currentStep, dto, assigner,
  2122. EWorkflowTraceType.Normal, expiredTime, isAutoFillSummaryOpinion, stepConfig, cancellationToken);
  2123. }
  2124. }
  2125. else
  2126. {
  2127. if (dto.BackToCountersignEnd)
  2128. {
  2129. // csStartStep.prev
  2130. var csStartStep = GetRealCsStartHandleStep(workflow.Steps, currentStep.CountersignStartStepId);
  2131. if (csStartStep is null)
  2132. throw new UserFriendlyException("未查询到会签节点");
  2133. nextSteps = await CreateCsEndStepsByTargetPrevAsync(workflow, csStartStep, dto, expiredTime,
  2134. isAutoFillSummaryOpinion, cancellationToken);
  2135. }
  2136. else
  2137. {
  2138. //依据会签策略创建会签下一级节点
  2139. nextSteps = await CreateCountersignStepsAsync(workflow, nextStepDefine, currentStep, dto,
  2140. assigner, expiredTime, stepConfig, cancellationToken);
  2141. }
  2142. }
  2143. }
  2144. else
  2145. {
  2146. if (dto.BackToCountersignEnd)
  2147. {
  2148. // check if cs all complete, create next
  2149. nextSteps = await CreateCsEndStepsByTargetPrevAsync(workflow, currentStep, dto,
  2150. expiredTime, isAutoFillSummaryOpinion, cancellationToken);
  2151. }
  2152. else
  2153. {
  2154. //依据会签策略创建会签下一级节点
  2155. nextSteps = await CreateCountersignStepsAsync(workflow, nextStepDefine, currentStep, dto,
  2156. assigner, expiredTime, stepConfig, cancellationToken);
  2157. }
  2158. }
  2159. }
  2160. else if (isStartCountersign) //top
  2161. {
  2162. //依据会签策略创建会签下一级节点
  2163. nextSteps = await CreateCountersignStepsAsync(workflow, nextStepDefine, currentStep, dto,
  2164. assigner, expiredTime, stepConfig, cancellationToken);
  2165. }
  2166. else if (isNextDynamic)
  2167. {
  2168. //创建动态下一级节点
  2169. nextSteps = await CreateDynamicStepsAsync(workflow, nextStepDefine, currentStep, dto, assigner,
  2170. expiredTime, stepConfig, cancellationToken);
  2171. }
  2172. else
  2173. {
  2174. //创建普通节点(根据配置)
  2175. nextSteps = await CreateConfigStepsAsync(workflow, nextStepDefine, currentStep, dto, assigner,
  2176. EWorkflowTraceType.Normal, expiredTime, isAutoFillSummaryOpinion, stepConfig, cancellationToken);
  2177. }
  2178. return nextSteps;
  2179. }
  2180. /// <summary>
  2181. /// 查询当前会签开始的第一级办理节点(会签汇总再次发起会签得场景)
  2182. /// </summary>
  2183. private static WorkflowStep? GetRealCsStartHandleStep(List<WorkflowStep> steps, string countersignStartStepId)
  2184. {
  2185. if (string.IsNullOrEmpty(countersignStartStepId))
  2186. throw new UserFriendlyException("会签汇总节点未正确赋值会签开始节点编号");
  2187. var csStartStep = steps.FirstOrDefault(d => d.Id == countersignStartStepId);
  2188. if (csStartStep.IsCountersignEndStep)
  2189. csStartStep = GetRealCsStartHandleStep(steps, csStartStep.CountersignStartStepId);
  2190. return csStartStep;
  2191. }
  2192. private async Task<List<WorkflowStep>> CreateDynamicStepsAsync(
  2193. Workflow workflow,
  2194. StepDefine nextStepDefine,
  2195. WorkflowStep prevStep,
  2196. BasicWorkflowDto dto,
  2197. UserInfo assigner,
  2198. DateTime? expiredTime,
  2199. Action<WorkflowStep>? stepConfig = null,
  2200. CancellationToken cancellationToken = default)
  2201. {
  2202. var handlerType = nextStepDefine.InstancePolicy switch
  2203. {
  2204. EDynamicPolicy.OrgUpCenterTop => EHandlerType.OrgLevel,
  2205. EDynamicPolicy.OrgUp => EHandlerType.OrgLevel,
  2206. EDynamicPolicy.OrgUpHandleCenterTop => EHandlerType.OrgLevel,
  2207. EDynamicPolicy.OrgUpHandle => EHandlerType.OrgLevel,
  2208. EDynamicPolicy.OrgUpLeadCenterTop => EHandlerType.OrgLevel,
  2209. EDynamicPolicy.OrgUpLead => EHandlerType.OrgLevel,
  2210. EDynamicPolicy.ArriveCenter => EHandlerType.OrgLevel,
  2211. EDynamicPolicy.ArriveOneOrg => EHandlerType.OrgLevel,
  2212. EDynamicPolicy.OrgDownCenterTop => EHandlerType.OrgLevel,
  2213. EDynamicPolicy.OrgDown => EHandlerType.OrgLevel,
  2214. null => throw new ArgumentOutOfRangeException(),
  2215. _ => throw new ArgumentOutOfRangeException()
  2216. };
  2217. return await CreateStepsAsync(workflow, nextStepDefine, prevStep, dto, assigner,
  2218. dto.NextHandlers, null, EWorkflowStepStatus.WaitForAccept,
  2219. ECountersignPosition.None, false, EWorkflowTraceType.Normal, handlerType, expiredTime,
  2220. stepConfig: stepConfig, cancellationToken: cancellationToken);
  2221. }
  2222. private Task<List<WorkflowStep>> CreateCountersignStepsAsync(
  2223. Workflow workflow,
  2224. StepDefine stepDefine,
  2225. WorkflowStep prevStep,
  2226. BasicWorkflowDto dto,
  2227. UserInfo assigner,
  2228. DateTime? expiredTime,
  2229. Action<WorkflowStep>? stepConfig = null,
  2230. CancellationToken cancellationToken = default
  2231. )
  2232. {
  2233. //var countersignId = dto.IsStartCountersign ? prevStep.StartCountersignId : prevStep.CountersignId;
  2234. var countersignId = prevStep.StartCountersignId;
  2235. //当前策略均为orglevel
  2236. var handlerType = EHandlerType.OrgLevel;
  2237. var nextStepCountersignPosition = dto.NextHandlers.Count > 1
  2238. ? ECountersignPosition.Direct
  2239. : ECountersignPosition.Indirect;
  2240. return CreateStepsAsync(workflow, stepDefine, prevStep, dto, assigner, dto.NextHandlers,
  2241. countersignId, EWorkflowStepStatus.WaitForAccept, nextStepCountersignPosition,
  2242. false, EWorkflowTraceType.Normal, handlerType, expiredTime, stepConfig: stepConfig, cancellationToken: cancellationToken);
  2243. }
  2244. /// <summary>
  2245. /// 根据传入节点的上一节点创建会签汇总节点(汇总传入节点的前一节点)
  2246. /// </summary>
  2247. private async Task<List<WorkflowStep>> CreateCsEndStepsByTargetPrevAsync(Workflow workflow, WorkflowStep step,
  2248. BasicWorkflowDto dto, DateTime? expiredTime, bool isAutoFillSummaryOpinion = false, CancellationToken cancellationToken = default)
  2249. {
  2250. //var countersignStartStep = workflow.Steps.FirstOrDefault(d => d.Id == step.PrevStepId);
  2251. var countersignStartStep = workflow.Steps.FirstOrDefault(d => d.StartCountersignId == step.CountersignId);
  2252. if (countersignStartStep is null)
  2253. throw new UserFriendlyException("未查询到当前节点上级会签开启节点");
  2254. var nextSteps = new List<WorkflowStep>();
  2255. //会签未全部办理则不创建汇总节点
  2256. //var csInnerSteps = workflow.Steps.Where(d => d.PrevStepId == countersignStartStep.Id).ToList();
  2257. //if (csInnerSteps.Any(d =>
  2258. // d.Status != EWorkflowStepStatus.Handled || (d.IsStartCountersign && !d.IsStartedCountersignEnd)))
  2259. if (!HasStepsAllHandled(workflow.Steps, countersignStartStep))
  2260. return nextSteps;
  2261. string? opinion = null;
  2262. if (isAutoFillSummaryOpinion)
  2263. {
  2264. //依据某节点作为根节点,查找最底层办理节点无论普通还是汇总节点
  2265. var preSteps = GetLastStepsFromRootStep(workflow.Steps, countersignStartStep);
  2266. var sb = new StringBuilder();
  2267. foreach (var prevStep in preSteps.OrderBy(d => d.HandleTime).ToList())
  2268. {
  2269. sb.AppendLine($"【会签时间】:{prevStep.HandleTime?.ToString("yyyy-MM-dd HH:mm:ss")}");
  2270. sb.AppendLine($"【会签人】:{prevStep.HandlerName}");
  2271. sb.AppendLine($"【会签结果】:{prevStep.Opinion}");
  2272. sb.AppendLine("");
  2273. }
  2274. opinion = sb.ToString();
  2275. }
  2276. // 创建会签汇总节点
  2277. var countersignEndStep =
  2278. await CreateCountersignEndStepAsync(workflow, countersignStartStep, dto, expiredTime, opinion, cancellationToken);
  2279. nextSteps = new List<WorkflowStep> { countersignEndStep };
  2280. //create trace
  2281. await CreateTraceAsync(workflow, countersignEndStep, EWorkflowTraceType.Normal, cancellationToken);
  2282. await _publisher.PublishAsync(new CountersignEndAssigned(workflow), PublishStrategy.ParallelWhenAll,
  2283. cancellationToken);
  2284. return nextSteps;
  2285. }
  2286. /// <summary>
  2287. /// 是否会签节点都已经办理完成(包含会签嵌套的判断)
  2288. /// </summary>
  2289. private bool HasStepsAllHandled(List<WorkflowStep> steps, WorkflowStep countersignStartStep)
  2290. {
  2291. var countersignSteps = GetStepsCascade(steps, countersignStartStep);
  2292. return countersignSteps.All(d => d.Status == EWorkflowStepStatus.Handled);
  2293. }
  2294. private List<WorkflowStep> GetStepsCascade(List<WorkflowStep> steps, WorkflowStep countersignStartStep)
  2295. {
  2296. _logger.LogWarning($"wfId: {countersignStartStep.WorkflowId}, stepId: {countersignStartStep.Id}");
  2297. var rsp = new List<WorkflowStep> { countersignStartStep };
  2298. if (!countersignStartStep.IsStartCountersign) return rsp;
  2299. var innerSteps = steps.Where(d => !string.IsNullOrEmpty(d.CountersignId)
  2300. && d.CountersignId == countersignStartStep.StartCountersignId).ToList();
  2301. _logger.LogWarning($"wfId: {countersignStartStep.WorkflowId}, innerSteps count: {innerSteps.Count}");
  2302. if (!innerSteps.Any()) return rsp;
  2303. foreach (var innerStep in innerSteps)
  2304. {
  2305. rsp.AddRange(GetStepsCascade(steps, innerStep));
  2306. }
  2307. return rsp;
  2308. }
  2309. /// <summary>
  2310. /// 以某一节点作为根节点开始查找最底层办理节点(需求:自动填充汇总节点意见)
  2311. /// </summary>
  2312. private List<WorkflowStep> GetLastStepsFromRootStep(List<WorkflowStep> steps, WorkflowStep rootStep)
  2313. {
  2314. var lastSteps = new List<WorkflowStep>();
  2315. var nextSteps = steps.Where(d => d.PrevStepId == rootStep.Id).ToList();
  2316. foreach (var nextStep in nextSteps)
  2317. {
  2318. if (nextStep.IsStartCountersign)
  2319. {
  2320. //find last csend
  2321. var lastCsEndStep = GetLastCountersignEndStep(steps, nextStep);
  2322. if (lastCsEndStep != null)
  2323. lastSteps.Add(lastCsEndStep);
  2324. }
  2325. else
  2326. {
  2327. lastSteps.Add(nextStep);
  2328. }
  2329. }
  2330. return lastSteps;
  2331. }
  2332. private WorkflowStep? GetLastCountersignEndStep(List<WorkflowStep> steps, WorkflowStep nextStep)
  2333. {
  2334. var csEndStep = steps.FirstOrDefault(d => d.IsCountersignEndStep && d.CountersignStartStepId == nextStep.Id);
  2335. if (csEndStep == null) return null;
  2336. return !csEndStep.IsStartCountersign ? csEndStep : GetLastCountersignEndStep(steps, csEndStep);
  2337. }
  2338. private async Task<WorkflowStep> CreateCountersignEndStepAsync(
  2339. Workflow workflow, WorkflowStep countersignStartStep,
  2340. BasicWorkflowDto dto, DateTime? expiredTime, string? opinion = null,
  2341. CancellationToken cancellationToken = default)
  2342. {
  2343. var csEndStep = _mapper.Map<WorkflowStep>(countersignStartStep);
  2344. csEndStep.Status = EWorkflowStepStatus.WaitForAccept;
  2345. csEndStep.PrevStepId = null;
  2346. csEndStep.PrevStepCode = null;
  2347. csEndStep.IsOrigin = false;
  2348. csEndStep.CountersignId = countersignStartStep.StartCountersignId;
  2349. csEndStep.CountersignPosition = ECountersignPosition.End;
  2350. //csEndStep.CountersignSteps = new();
  2351. csEndStep.IsCountersignEndStep = true;
  2352. csEndStep.CountersignStartStepId = countersignStartStep.Id;
  2353. csEndStep.Name = dto.NextStepName;
  2354. //csEndStep.TimeLimit = GetTimeLimit("");
  2355. csEndStep.StepExpiredTime = expiredTime;
  2356. csEndStep.BusinessType = dto.BusinessType;
  2357. csEndStep.Handlers = countersignStartStep.Handlers
  2358. .Where(d => d.Key == countersignStartStep.HandlerId || d.Key == countersignStartStep.HandlerOrgId)
  2359. .ToList();
  2360. //需求调整:汇总节点指派给发起人部门办理 //todo 待重构
  2361. csEndStep.FlowAssignType = EFlowAssignType.Org;
  2362. csEndStep.Reset();
  2363. csEndStep.ResetParameters();
  2364. if (!string.IsNullOrEmpty(opinion))
  2365. csEndStep.Opinion = opinion;
  2366. await _workflowStepRepository.AddAsync(csEndStep, cancellationToken);
  2367. workflow.Steps.Add(csEndStep);
  2368. //await _workflowStepRepository.AddNav(csEndStep)
  2369. // .Include(d => d.StepHandlers)
  2370. // .ExecuteCommandAsync();
  2371. return csEndStep;
  2372. }
  2373. private bool CheckIsActualHandle(Workflow workflow, WorkflowStep step, StepDefine nextStepDefine,
  2374. BasicWorkflowDto dto)
  2375. {
  2376. //1. workflow是否为办理类型 2. 非会签:当前是否为普通节点and下一节点是否为汇总 or endStep
  2377. //3. 会签:当前操作为汇总还是继续往下办理?thk: 汇总以后但未回到top又往下办理的场景,前面实际办理部门也算作办理部门
  2378. if (workflow.FlowType is not EFlowType.Handle) return false;
  2379. if (workflow.IsInCountersign)
  2380. {
  2381. return !step.IsCountersignEndStep && dto.BackToCountersignEnd;
  2382. }
  2383. else
  2384. {
  2385. return step.StepType is EStepType.Normal &&
  2386. nextStepDefine.StepType is EStepType.Summary or EStepType.End;
  2387. }
  2388. }
  2389. /// <summary>
  2390. /// 办理节点(赋值节点的办理对象信息)
  2391. /// </summary>
  2392. private void HandleStep(WorkflowStep step, EHandleMode handleMode, string opinion, string nextStepCode)
  2393. {
  2394. //todo 重构:ISessionContext传入
  2395. step.Handle(_sessionContextProvider.SessionContext.RequiredUserId, _sessionContextProvider.SessionContext.UserName,
  2396. _sessionContextProvider.SessionContext.RequiredOrgId, _sessionContextProvider.SessionContext.OrgName,
  2397. _sessionContextProvider.SessionContext.OrgAreaCode, _sessionContextProvider.SessionContext.OrgAreaName,
  2398. _sessionContextProvider.SessionContext.OrgIsCenter, handleMode, opinion, nextStepCode);
  2399. //var handler = step.FindActualHandler(current.Roles, current.RequiredUserId, current.RequiredOrgId);
  2400. //if (handler is not null)
  2401. // handler.IsActualHandler = true;
  2402. }
  2403. /// <summary>
  2404. /// 开始会签(创建会签数据,更新currentStep会签数据)
  2405. /// </summary>
  2406. private async Task<WorkflowCountersign> StartCountersignAsync(ISessionContext current, Workflow workflow, WorkflowStep startStep,
  2407. BasicWorkflowDto dto, ECounterSignType? counterSignType, DateTime? expiredTime,
  2408. CancellationToken cancellationToken)
  2409. {
  2410. var countersignId = startStep.CountersignId;
  2411. if (startStep.IsCountersignEndStep)
  2412. {
  2413. var topStartCsStep = GetCsLoopStartStep(workflow.Steps, startStep);
  2414. countersignId = topStartCsStep.CountersignId;
  2415. }
  2416. var countersign = await CreateCountersignAsync(current, workflow, startStep,
  2417. dto.NextHandlers.Select(d => new Kv(d.Key, d.Value)).ToList(), dto.FlowAssignType,
  2418. counterSignType, expiredTime, countersignId, cancellationToken);
  2419. startStep.StartCountersign(countersign.Id);
  2420. return countersign;
  2421. }
  2422. /// <summary>
  2423. /// 检查是否从中心流转至部门
  2424. /// </summary>
  2425. private bool CheckIfFlowFromCenterToOrg(WorkflowStep sourceStep, StepDefine targetStepBoxDefine)
  2426. {
  2427. var isFromCenter = sourceStep.IsCenter();
  2428. if (!isFromCenter) return false;
  2429. var isToOrg = targetStepBoxDefine.IsOrg();
  2430. return isFromCenter && isToOrg;
  2431. }
  2432. /// <summary>
  2433. /// 检查是否从中心流转至部门
  2434. /// </summary>
  2435. private bool CheckIfFlowFromCenterToOrg(Workflow workflow, WorkflowStep targetStepBox)
  2436. {
  2437. var isToOrg = targetStepBox.IsOrg();
  2438. if (!isToOrg) return false;
  2439. var isFromCenter = workflow.Steps.All(d => d.BusinessType is not EBusinessType.Department);
  2440. return isFromCenter && isToOrg;
  2441. }
  2442. /// <summary>
  2443. /// 检查是否从部门流转至中心
  2444. /// </summary>
  2445. private bool CheckIfFlowFromOrgToCenter(WorkflowStep sourceStepBox, StepDefine targetStepBoxDefine)
  2446. {
  2447. var isFromOrg = sourceStepBox.IsOrg();
  2448. if (!isFromOrg) return false;
  2449. var isToCenter = targetStepBoxDefine.IsCenter();
  2450. return isFromOrg && isToCenter;
  2451. }
  2452. /// <summary>
  2453. /// 检查是否从部门流转至中心
  2454. /// </summary>
  2455. private bool CheckIfFlowFromOrgToCenter(WorkflowStep sourceStepBox, WorkflowStep targetStep)
  2456. {
  2457. var isFromOrg = sourceStepBox.IsOrg();
  2458. if (!isFromOrg) return false;
  2459. var isToCenter = targetStep.IsCenter();
  2460. return isFromOrg && isToCenter;
  2461. }
  2462. /// <summary>
  2463. /// 检查是否从部门流转至中心
  2464. /// </summary>
  2465. private bool CheckIfFlowFromOrgToCenter(Workflow workflow, WorkflowStep targetStep)
  2466. {
  2467. var isToCenter = targetStep.IsCenter();
  2468. if (!isToCenter) return false;
  2469. var isFromOrg = workflow.Steps.Any(d => d.BusinessType is EBusinessType.Department or EBusinessType.DepartmentLeader);
  2470. return isFromOrg && isToCenter;
  2471. }
  2472. /// <summary>
  2473. /// 复制一个节点为待接办
  2474. /// </summary>
  2475. private async Task<WorkflowStep> DuplicateStepWithTraceAsync(Workflow workflow, WorkflowStep step,
  2476. EWorkflowTraceType traceType, StepAssignInfo? stepAssignInfo, DateTime expiredTime, CancellationToken cancellationToken)
  2477. {
  2478. var newStep = DuplicateStep(step, traceType, stepAssignInfo, expiredTime);
  2479. await _workflowStepRepository.AddAsync(newStep, cancellationToken);
  2480. await CreateTraceAsync(workflow, newStep, traceType, cancellationToken);
  2481. return newStep;
  2482. }
  2483. private WorkflowStep DuplicateStep(WorkflowStep step, EWorkflowTraceType traceType, StepAssignInfo? stepAssignInfo, DateTime? expiredTime)
  2484. {
  2485. var newStep = _mapper.Map<WorkflowStep>(step);
  2486. newStep.Reset();
  2487. newStep.Status = EWorkflowStepStatus.WaitForAccept;
  2488. newStep.PrevStepId = step.PrevStepId;
  2489. newStep.IsMain = step.IsMain;
  2490. newStep.IsOrigin = step.IsOrigin;
  2491. //newStep.ParentId = step.ParentId;
  2492. newStep.Handlers = step.Handlers;
  2493. newStep.StartCountersignId = step.StartCountersignId;
  2494. newStep.CountersignId = step.CountersignId;
  2495. newStep.IsStartedCountersignEnd = step.IsStartedCountersignEnd;
  2496. newStep.StepExpiredTime = expiredTime;
  2497. newStep.InitId();
  2498. if (stepAssignInfo is not null)
  2499. newStep.Assign(stepAssignInfo);
  2500. ////退回场景:指派给原办理人,其余场景:按照原节点原始指派方式复制 //todo 重构为参数传入办理对象
  2501. //if (traceType is EWorkflowTraceType.Previous)
  2502. //{
  2503. // newStep.FlowAssignType = step.FlowAssignType;
  2504. // //newStep.FlowAssignType = EFlowAssignType.User;
  2505. // // 是否中心 临时紧急修改 后续在流程模版定义是否原办理人退回类型 来实现流程 禅道200
  2506. // //newStep.FlowAssignType = step.BusinessType is EBusinessType.Seat or EBusinessType.Send
  2507. // // ? step.BusinessType is EBusinessType.Send ? EFlowAssignType.User : EFlowAssignType.Role
  2508. // // : EFlowAssignType.Org;
  2509. // //if (newStep is { FlowAssignType: EFlowAssignType.Role, BusinessType: EBusinessType.Send })
  2510. // // newStep.FlowAssignType = EFlowAssignType.User;
  2511. // newStep.Assign(step.HandlerId, step.HandlerName, step.HandlerOrgId, step.HandlerOrgName, step.RoleId, step.RoleName);
  2512. //}
  2513. return newStep;
  2514. }
  2515. private async Task<WorkflowCountersign> CreateCountersignAsync(
  2516. ISessionContext current, Workflow workflow, WorkflowStep startStep, List<Kv> handlers,
  2517. EFlowAssignType? flowAssignType, ECounterSignType? counterSignType, DateTime? expiredTime,
  2518. string? parentId = null, CancellationToken cancellationToken = default)
  2519. {
  2520. var members = handlers.Select(d => new WorkflowCountersignMember
  2521. {
  2522. Key = d.Key,
  2523. Value = d.Value,
  2524. FlowAssignType = flowAssignType
  2525. }).ToList();
  2526. var countersign = new WorkflowCountersign
  2527. {
  2528. WorkflowId = workflow.Id,
  2529. StartStepId = startStep.Id,
  2530. StartStepCode = startStep.Code,
  2531. StartStepBusiType = startStep.BusinessType,
  2532. StarterId = current.UserId,
  2533. StarterName = current.UserName ?? string.Empty,
  2534. StarterOrgId = current.OrgId,
  2535. StarterOrgName = current.OrgName,
  2536. StarterOrgAreaCode = current.OrgAreaCode ?? string.Empty,
  2537. StarterOrgAreaName = current.OrgAreaName ?? string.Empty,
  2538. ParentId = parentId,
  2539. Members = members,
  2540. FlowAssignType = flowAssignType,
  2541. CounterSignType = counterSignType,
  2542. ExpiredTime = expiredTime,
  2543. //ExternalId = workflow.ExternalId,
  2544. };
  2545. //await _workflowCountersignRepository.AddAsync(countersign, cancellationToken);
  2546. await _workflowCountersignRepository.AddNav(countersign)
  2547. .Include(d => d.Members)
  2548. .ExecuteCommandAsync();
  2549. return countersign;
  2550. }
  2551. //private async Task JumpTraceAsync(string workflowId, RecallDto dto, CancellationToken cancellationToken)
  2552. //{
  2553. // //未办理的traces
  2554. // var uncompleteTraces =
  2555. // await _workflowTraceRepository.QueryAsync(d =>
  2556. // d.WorkflowId == workflowId && string.IsNullOrEmpty(d.HandlerId));
  2557. // foreach (var trace in uncompleteTraces)
  2558. // {
  2559. // HandleTrace(trace, dto.Opinion);
  2560. // }
  2561. // await _workflowTraceRepository.UpdateRangeAsync(uncompleteTraces, cancellationToken);
  2562. //}
  2563. //private async Task RecallTraceAsync(List<WorkflowTrace> traces, string opinion, ISessionContext current,
  2564. // CancellationToken cancellationToken)
  2565. //{
  2566. // //未办理的traces
  2567. // //var uncompleteTraces =
  2568. // // await _workflowTraceRepository.QueryAsync(d =>
  2569. // // d.WorkflowId == workflowId && string.IsNullOrEmpty(d.HandlerId));
  2570. // var uncompleteTraces = traces.Where(d => d.Status != EWorkflowStepStatus.Handled).ToList();
  2571. // if (uncompleteTraces.Any())
  2572. // {
  2573. // foreach (var trace in uncompleteTraces)
  2574. // {
  2575. // trace.Handle(
  2576. // current.RequiredUserId, current.UserName,
  2577. // current.RequiredOrgId, current.OrgName,
  2578. // current.OrgAreaCode, current.OrgAreaName,
  2579. // current.OrgIsCenter, opinion);
  2580. // }
  2581. // await _workflowTraceRepository.UpdateRangeAsync(uncompleteTraces, cancellationToken);
  2582. // }
  2583. //}
  2584. //private async Task<WorkflowTrace> PreviousTraceAsync(string workflowId,
  2585. // PreviousWorkflowDto dto, WorkflowStep step,
  2586. // string applicantId, string applicantName,
  2587. // string applicantOrgId, string applicantOrgName,
  2588. // string applicantOrgAreaCode, string applicantOrgAreaName,
  2589. // bool applicantIsCenter, CancellationToken cancellationToken)
  2590. //{
  2591. // var trace = await GetWorkflowTraceAsync(workflowId, step.Id, cancellationToken);
  2592. // _mapper.Map(dto, trace);
  2593. // //HandleTrace(trace, dto.Opinion, current);
  2594. // trace.Handle(applicantId, applicantName,
  2595. // applicantOrgId, applicantOrgName,
  2596. // applicantOrgAreaCode, applicantOrgAreaName,
  2597. // applicantIsCenter, dto.Opinion);
  2598. // await _workflowTraceRepository.UpdateAsync(trace, cancellationToken);
  2599. // return trace;
  2600. //}
  2601. //private async Task EndTraceAsync(Workflow workflow, BasicWorkflowDto dto, WorkflowStep step, CancellationToken cancellationToken)
  2602. //{
  2603. // var trace = _mapper.Map<WorkflowTrace>(step);
  2604. // trace.Status = EWorkflowTraceStatus.Normal;
  2605. // trace.ExpiredTime = workflow.ExpiredTime;
  2606. // trace.TimeLimit = workflow.TimeLimit;
  2607. // await _workflowTraceRepository.AddAsync(trace, cancellationToken);
  2608. //}
  2609. private async Task<WorkflowTrace> NextTraceAsync(Workflow workflow, BasicWorkflowDto dto, WorkflowStep step,
  2610. CancellationToken cancellationToken)
  2611. {
  2612. // var trace = await GetWorkflowTraceAsync(workflow.Id, step.Id, cancellationToken);
  2613. var trace = workflow.Traces.FirstOrDefault(d => d.Id == step.Id);
  2614. if (trace == null)
  2615. throw new UserFriendlyException($"未找到对应trace, workflowId: {workflow.Id}, stepId: {step.Id}");
  2616. _mapper.Map(dto, trace);
  2617. _mapper.Map(step, trace);
  2618. await _workflowTraceRepository.UpdateAsync(trace, cancellationToken);
  2619. return trace;
  2620. }
  2621. //private async Task AcceptTraceAsync(Workflow workflow, WorkflowStep step, CancellationToken cancellationToken)
  2622. //{
  2623. // var trace = await GetWorkflowTraceAsync(workflow.Id, step.Id, cancellationToken);
  2624. // _mapper.Map(step, trace);
  2625. // await _workflowTraceRepository.UpdateAsync(trace, cancellationToken);
  2626. //}
  2627. private async Task CreateTraceAsync(Workflow workflow, WorkflowStep step,
  2628. EWorkflowTraceType traceType = EWorkflowTraceType.Normal,
  2629. CancellationToken cancellationToken = default)
  2630. {
  2631. var sendHandleTimes = 0;
  2632. if (step.BusinessType == EBusinessType.Send)
  2633. {
  2634. var sendHandleCount = workflow.Traces.Count(d => d.StepType == EStepType.Normal &&
  2635. d.BusinessType == EBusinessType.Send);
  2636. sendHandleTimes = sendHandleCount + 1;
  2637. }
  2638. var trace = _mapper.Map<WorkflowTrace>(step);
  2639. trace.TraceType = traceType;
  2640. trace.SendHandleTimes = sendHandleTimes;
  2641. if (workflow.IsInCountersign && step.IsInCountersign())
  2642. {
  2643. if (step.IsCountersignEndStep)
  2644. {
  2645. //var startTrace = await GetWorkflowTraceAsync(workflow.Id, step.CountersignStartStepId, cancellationToken);
  2646. var startTrace = workflow.Traces.FirstOrDefault(d => d.Id == step.CountersignStartStepId);
  2647. if (startTrace == null)
  2648. throw new UserFriendlyException(
  2649. $"未找到startTrace, workflowId: {workflow.Id}, step.CountersignStartStepId: {step.CountersignStartStepId}");
  2650. trace.ParentId = startTrace.ParentId;
  2651. }
  2652. else
  2653. {
  2654. //if (step.CountersignPosition is ECountersignPosition.Direct)
  2655. //{
  2656. // var prevTrace = await GetWorkflowTraceAsync(workflow.Id, step.PrevStepId, cancellationToken);
  2657. // trace.ParentId = prevTrace.Id;
  2658. //}
  2659. //else if (step.CountersignPosition is ECountersignPosition.Single)
  2660. //{
  2661. // var prevTrace = await GetWorkflowTraceAsync(workflow.Id, step.PrevStepId, cancellationToken);
  2662. // trace.ParentId = prevTrace.ParentId;
  2663. //}
  2664. //var prevTrace = await GetWorkflowTraceAsync(workflow.Id, step.PrevStepId, cancellationToken);
  2665. var prevTrace = workflow.Traces.FirstOrDefault(d => d.Id == step.PrevStepId);
  2666. if (prevTrace == null)
  2667. throw new UserFriendlyException($"未找到prevTrace, workflowId: {workflow.Id}, step.PrevStepId: {step.PrevStepId}");
  2668. trace.ParentId = prevTrace.Id;
  2669. }
  2670. }
  2671. await _workflowTraceRepository.AddAsync(trace, cancellationToken);
  2672. workflow.Traces.Add(trace);
  2673. }
  2674. private async Task<WorkflowTrace> GetWorkflowTraceAsync(string workflowId, string stepId,
  2675. CancellationToken cancellationToken)
  2676. {
  2677. var parentTrace = await _workflowTraceRepository.GetAsync(d =>
  2678. d.WorkflowId == workflowId && d.StepId == stepId, cancellationToken);
  2679. if (parentTrace == null)
  2680. throw new UserFriendlyException($"未找到对应trace, workflowId: {workflowId}, stepId: {stepId}");
  2681. return parentTrace;
  2682. }
  2683. private async Task<bool> RecallAsync(Workflow workflow, BasicWorkflowDto dto, FlowAssignInfo flowAssignInfo,
  2684. StepDefine targetStepDefine, WorkflowStep targetStep, EWorkflowTraceType traceType,
  2685. DateTime? expiredTime, bool isOrderFiled, EHandleMode handleMode, CancellationToken cancellationToken)
  2686. {
  2687. var targetIsStartStep = targetStepDefine.StepType is EStepType.Start;
  2688. var updateTraces = new List<WorkflowTrace>();
  2689. //update uncomplete traces
  2690. var uncompleteTraces = workflow.Traces.Where(d => d.Status != EWorkflowStepStatus.Handled && d.TraceStyle == ETraceStyle.Flow).ToList();
  2691. if (uncompleteTraces.Any())
  2692. {
  2693. foreach (var trace in uncompleteTraces)
  2694. {
  2695. trace.Handle(
  2696. _sessionContextProvider.SessionContext.RequiredUserId, _sessionContextProvider.SessionContext.UserName,
  2697. _sessionContextProvider.SessionContext.RequiredOrgId, _sessionContextProvider.SessionContext.OrgName,
  2698. _sessionContextProvider.SessionContext.OrgAreaCode, _sessionContextProvider.SessionContext.OrgAreaName,
  2699. _sessionContextProvider.SessionContext.OrgIsCenter, handleMode, dto.Opinion);
  2700. }
  2701. //await _workflowTraceRepository.UpdateRangeAsync(uncompleteTraces, cancellationToken);
  2702. updateTraces.AddRange(uncompleteTraces);
  2703. }
  2704. else
  2705. {
  2706. var endTrace = workflow.Traces.Where(d => d.StepType == EStepType.End && d.TraceStyle == ETraceStyle.Flow).MaxBy(d => d.CreationTime);
  2707. if (endTrace is not null)
  2708. {
  2709. endTrace.Opinion += ("\r\n" + dto.Opinion);
  2710. updateTraces.Add(endTrace);
  2711. }
  2712. }
  2713. //get targetStep's previous
  2714. WorkflowStep? targetPrevStep = null;
  2715. if (!targetIsStartStep)
  2716. {
  2717. targetPrevStep = workflow.Steps.FirstOrDefault(d => d.Id == targetStep.PrevStepId);
  2718. if (targetPrevStep == null)
  2719. throw new UserFriendlyException($"{nameof(RecallAsync)}, 未找到目标节点的前一节点, flowId: {workflow.Id}");
  2720. }
  2721. //查询所有目标节点之后的节点,然后删掉(包括目标节点)
  2722. var removeSteps = GetStepsBehindTargetStep(workflow.Steps, targetStep);
  2723. if (removeSteps.Any())
  2724. {
  2725. await _workflowStepRepository.RemoveRangeAsync(removeSteps, cancellationToken);
  2726. workflow.Steps.RemoveAll(d => removeSteps.Contains(d));
  2727. //更新快照对应节点状态
  2728. var stepIds = removeSteps.Select(d => d.Id).ToList();
  2729. var traces = workflow.Traces.Where(d => stepIds.Contains(d.StepId)).ToList();
  2730. //await UpdateTracesStateAsync(updateTraces, EWorkflowTraceState.StepRemoveByRecall, cancellationToken);
  2731. foreach (var trace in traces)
  2732. {
  2733. trace.TraceState = isOrderFiled
  2734. ? EWorkflowTraceState.StepRemoveByRecallWhenFiled
  2735. : EWorkflowTraceState.StepRemoveByRecall;
  2736. }
  2737. updateTraces.AddRange(traces);
  2738. }
  2739. await _workflowTraceRepository.UpdateRangeAsync(updateTraces, cancellationToken);
  2740. //结束会签
  2741. var unCompleteCountersigns = workflow.Countersigns.Where(d => !d.IsCompleted()).ToList();
  2742. if (unCompleteCountersigns.Any())
  2743. {
  2744. foreach (var unCompleteCountersign in unCompleteCountersigns)
  2745. {
  2746. unCompleteCountersign.End(null, null, EBusinessType.File,
  2747. _sessionContextProvider.SessionContext.RequiredUserId, _sessionContextProvider.SessionContext.UserName,
  2748. _sessionContextProvider.SessionContext.RequiredOrgId, _sessionContextProvider.SessionContext.OrgName,
  2749. _sessionContextProvider.SessionContext.OrgAreaCode, _sessionContextProvider.SessionContext.OrgAreaName);
  2750. }
  2751. await _workflowCountersignRepository.UpdateRangeAsync(unCompleteCountersigns, cancellationToken);
  2752. }
  2753. workflow.EndCountersign();
  2754. workflow.ResetOption();
  2755. if (workflow.Status is EWorkflowStatus.Completed)
  2756. workflow.SetStatusRunnable();
  2757. var assigner = new UserInfo(
  2758. _sessionContextProvider.SessionContext.UserId,
  2759. _sessionContextProvider.SessionContext.UserName,
  2760. _sessionContextProvider.SessionContext.OrgId,
  2761. _sessionContextProvider.SessionContext.OrgName,
  2762. _sessionContextProvider.SessionContext.OrgIsCenter
  2763. );
  2764. var targetStepNew = targetIsStartStep
  2765. ? await CreateStartStepAsync(workflow, targetStepDefine, dto, assigner,
  2766. dto.NextHandlers.First(), traceType, expiredTime, flowAssignInfo.FlowAssignType, cancellationToken)
  2767. : (await CreateStepsAsync(workflow, targetStepDefine, targetPrevStep, dto, assigner, dto.NextHandlers,
  2768. null, EWorkflowStepStatus.WaitForAccept, ECountersignPosition.None, true, traceType,
  2769. null, expiredTime, cancellationToken: cancellationToken)).First();
  2770. //更新实际办理节点信息
  2771. workflow.UpdateActualStepWhenAssign(targetStepNew, new FlowStepHandler
  2772. {
  2773. UserId = targetStep.HandlerId,
  2774. Username = targetStep.HandlerName,
  2775. OrgId = targetStep.HandlerOrgId,
  2776. OrgName = targetStep.HandlerOrgName
  2777. });
  2778. workflow.UpdateCurrentStepWhenAssign(targetStepNew, new FlowStepHandler
  2779. {
  2780. UserId = targetStep.HandlerId,
  2781. Username = targetStep.HandlerName,
  2782. OrgId = targetStep.HandlerOrgId,
  2783. OrgName = targetStep.HandlerOrgName
  2784. });
  2785. //取消维护workflow得冗余字段(FlowedOrgIds, HandlerOrgs)
  2786. //workflow.Assign(flowAssignInfo.FlowAssignType, flowAssignInfo.GetHandlerIds());
  2787. //workflow.ResetHandlers(flowAssignInfo.FlowAssignType, flowAssignInfo.HandlerObjects);
  2788. //calc workflow expired time
  2789. var isOrgToCenter = CheckIfFlowFromOrgToCenter(workflow, targetStep);
  2790. return isOrgToCenter;
  2791. }
  2792. private List<WorkflowStep> GetStepsBehindTargetStep(List<WorkflowStep> steps, WorkflowStep targetStep)
  2793. {
  2794. var behindSteps = new List<WorkflowStep> { targetStep };
  2795. if (!steps.Any()) return behindSteps;
  2796. var nextSteps = targetStep.IsStartCountersign
  2797. ? steps.Where(d => d.CountersignId == targetStep.StartCountersignId).ToList()
  2798. : steps.Where(d => d.PrevStepId == targetStep.Id).ToList();
  2799. //var nextSteps = steps.Where(d => d.PrevStepId == targetStep.Id).ToList();
  2800. if (!nextSteps.Any())
  2801. return behindSteps;
  2802. foreach (var nextStep in nextSteps)
  2803. {
  2804. var leftSteps = steps.Except(behindSteps).ToList();
  2805. behindSteps.AddRange(GetStepsBehindTargetStep(leftSteps, nextStep));
  2806. }
  2807. return behindSteps;
  2808. }
  2809. private static void CheckWhetherRunnable(EWorkflowStatus status)
  2810. {
  2811. if (status != EWorkflowStatus.Runnable)
  2812. throw UserFriendlyException.SameMessage("当前流程状态不可继续流转");
  2813. }
  2814. private void ValidatePermission(Workflow workflow, string OrgId, string UserId, string[] roleIds)
  2815. {
  2816. if (!workflow.IsCanHandle(UserId, OrgId, roleIds))
  2817. throw UserFriendlyException.SameMessage("无办理权限");
  2818. }
  2819. private async Task<WorkflowStep> CreateEndStepAsync(
  2820. Workflow workflow,
  2821. StepDefine endStepDefine,
  2822. WorkflowStep prevStep,
  2823. BasicWorkflowDto dto,
  2824. DateTime? expiredTime,
  2825. CancellationToken cancellationToken)
  2826. {
  2827. if (workflow.Steps.Any(d => d.StepType == EStepType.End))
  2828. throw UserFriendlyException.SameMessage("无法重复创建结束节点");
  2829. var handler = new FlowStepHandler
  2830. {
  2831. Key = _sessionContextProvider.SessionContext.UserId,
  2832. Value = _sessionContextProvider.SessionContext.UserName,
  2833. UserId = _sessionContextProvider.SessionContext.RequiredUserId,
  2834. Username = _sessionContextProvider.SessionContext.UserName,
  2835. OrgId = _sessionContextProvider.SessionContext.OrgId,
  2836. OrgName = _sessionContextProvider.SessionContext.OrgName,
  2837. };
  2838. var assigner = new UserInfo(
  2839. _sessionContextProvider.SessionContext.UserId,
  2840. _sessionContextProvider.SessionContext.UserName,
  2841. _sessionContextProvider.SessionContext.OrgId,
  2842. _sessionContextProvider.SessionContext.OrgName,
  2843. _sessionContextProvider.SessionContext.OrgIsCenter
  2844. );
  2845. var step = CreateStep(workflow, endStepDefine, prevStep, EFlowAssignType.User, handler, assigner,
  2846. null, null, EWorkflowStepStatus.WaitForAccept,
  2847. ECountersignPosition.None, expiredTime, endStepDefine.Name, true, businessType: EBusinessType.File, flowDirection: dto.FlowDirection);
  2848. //step.Accept(_sessionContextProvider.SessionContext.RequiredUserId, _sessionContextProvider.SessionContext.UserName,
  2849. // _sessionContextProvider.SessionContext.RequiredOrgId, _sessionContextProvider.SessionContext.OrgName,
  2850. // _sessionContextProvider.SessionContext.OrgAreaCode, _sessionContextProvider.SessionContext.OrgAreaName);
  2851. HandleStep(step, EHandleMode.Normal, "流程归档", string.Empty);
  2852. await _workflowStepRepository.AddAsync(step, cancellationToken);
  2853. workflow.Steps.Add(step);
  2854. //end trace
  2855. await CreateTraceAsync(workflow, step, cancellationToken: cancellationToken);
  2856. return step;
  2857. }
  2858. public async Task<List<WorkflowStep>> CreateConfigStepsAsync(
  2859. Workflow workflow,
  2860. StepDefine stepDefine,
  2861. WorkflowStep prevStep,
  2862. BasicWorkflowDto dto,
  2863. UserInfo assigner,
  2864. EWorkflowTraceType traceType,
  2865. DateTime? expiredTime,
  2866. bool isAutoFillSummaryOpinion = false,
  2867. Action<WorkflowStep>? stepConfig = null,
  2868. CancellationToken cancellationToken = default)
  2869. {
  2870. // List<FlowStepHandler> handlers;
  2871. // if (stepDefine.HandlerType != EHandlerType.Role && !dto.NextHandlers.Any())
  2872. // throw new UserFriendlyException("未指定节点处理者");
  2873. // if (stepDefine.HandlerType == EHandlerType.Role && !dto.NextHandlers.Any())
  2874. // {
  2875. // var handler = stepDefine.HandlerTypeItems.First();
  2876. // handlers = new List<FlowStepHandler>
  2877. // {
  2878. // new() { Key = handler.Key, Value = handler.Value, RoleId = handler.Key, RoleName = handler.Value }
  2879. // };
  2880. // }
  2881. // else
  2882. // {
  2883. // handlers = dto.NextHandlers;
  2884. // }
  2885. string? opinion = null;
  2886. if (isAutoFillSummaryOpinion && stepDefine.StepType is EStepType.Summary)
  2887. opinion = prevStep.Opinion;
  2888. return await CreateStepsAsync(workflow, stepDefine, prevStep, dto,
  2889. assigner, dto.NextHandlers, null,
  2890. EWorkflowStepStatus.WaitForAccept, ECountersignPosition.None,
  2891. true, traceType, null, expiredTime, opinion, stepConfig, cancellationToken);
  2892. }
  2893. private async Task<List<WorkflowStep>> CreateStepsAsync(
  2894. Workflow workflow,
  2895. StepDefine stepDefine,
  2896. WorkflowStep prevStep,
  2897. BasicWorkflowDto dto,
  2898. //bool isStartCountersign,
  2899. UserInfo assigner,
  2900. List<FlowStepHandler> handlers,
  2901. string? countersignId,
  2902. EWorkflowStepStatus stepStatus,
  2903. ECountersignPosition csPosition,
  2904. bool isOrigin,
  2905. EWorkflowTraceType traceType,
  2906. EHandlerType? handlerType = null,
  2907. DateTime? expiredTime = null,
  2908. string? opinion = null,
  2909. Action<WorkflowStep>? stepConfig = null,
  2910. CancellationToken cancellationToken = default
  2911. )
  2912. {
  2913. List<WorkflowStep> steps = new();
  2914. foreach (var handler in handlers)
  2915. {
  2916. var isMain = handlers.Count == 1 || (handlers.Count > 1 && handler.Key == dto.NextMainHandler);
  2917. var step = CreateStep(workflow, stepDefine, prevStep, dto.FlowAssignType,
  2918. handler, assigner, dto.NextStepCode, countersignId, stepStatus, csPosition, expiredTime,
  2919. dto.NextStepName, isOrigin, isMain, handlerType, dto.BusinessType, dto.FlowDirection, opinion);
  2920. stepConfig?.Invoke(step);
  2921. steps.Add(step);
  2922. }
  2923. await _workflowStepRepository.AddRangeAsync(steps, cancellationToken);
  2924. workflow.Steps.AddRange(steps);
  2925. //create traces todo add range traces
  2926. foreach (var step in steps)
  2927. {
  2928. await CreateTraceAsync(workflow, step, traceType, cancellationToken);
  2929. }
  2930. return steps;
  2931. }
  2932. private async Task<WorkflowTrace> CreatePublishTraceAsync(WorkflowTrace endTrace, CancellationToken cancellation)
  2933. {
  2934. var pubTrace = _mapper.Map<WorkflowTrace>(endTrace);
  2935. pubTrace.TraceStyle = ETraceStyle.Publish;
  2936. pubTrace.Name = "中心发布";
  2937. pubTrace.Status = EWorkflowStepStatus.WaitForAccept;
  2938. pubTrace.Code = "publish";
  2939. pubTrace.CreationTime = endTrace.HandleTime ?? DateTime.Now;
  2940. pubTrace.PrevStepId = endTrace.Id;
  2941. pubTrace.PrevStepCode = endTrace.Code;
  2942. pubTrace.PrevStepName = endTrace.Name;
  2943. pubTrace.AcceptorId = null;
  2944. pubTrace.AcceptorName = null;
  2945. pubTrace.AcceptorOrgId = null;
  2946. pubTrace.AcceptorOrgName = null;
  2947. pubTrace.AcceptorOrgAreaCode = null;
  2948. pubTrace.AcceptorOrgAreaName = null;
  2949. pubTrace.AcceptTime = null;
  2950. pubTrace.HandlerId = null;
  2951. pubTrace.HandlerName = null;
  2952. pubTrace.HandlerOrgId = null;
  2953. pubTrace.HandlerOrgName = null;
  2954. pubTrace.HandlerOrgAreaCode = null;
  2955. pubTrace.HandlerOrgAreaName = null;
  2956. pubTrace.HandleTime = null;
  2957. pubTrace.StepExpiredTime = null;
  2958. pubTrace.CreationTime = endTrace.CreationTime.AddSeconds(1);
  2959. await _workflowTraceRepository.AddAsync(pubTrace, cancellation);
  2960. return pubTrace;
  2961. }
  2962. private async Task<WorkflowTrace> CreateVisitTraceAsync(WorkflowTrace pubTrace, UserInfo acceptor, string orderVisitId,
  2963. CancellationToken cancellation)
  2964. {
  2965. if (string.IsNullOrEmpty(orderVisitId))
  2966. throw new UserFriendlyException($"参数异常,orderVisitId不能为空, pubTraceId: {pubTrace.Id}");
  2967. var visitTrace = _mapper.Map<WorkflowTrace>(pubTrace);
  2968. visitTrace.OrderPublishId = null;
  2969. visitTrace.OrderVisitId = orderVisitId;
  2970. visitTrace.TraceStyle = ETraceStyle.Visit;
  2971. visitTrace.Name = "中心回访";
  2972. visitTrace.Status = EWorkflowStepStatus.WaitForAccept;
  2973. visitTrace.Code = "visit";
  2974. visitTrace.CreationTime = pubTrace.HandleTime ?? DateTime.Now;
  2975. visitTrace.PrevStepId = pubTrace.Id;
  2976. visitTrace.PrevStepCode = pubTrace.Code;
  2977. visitTrace.PrevStepName = pubTrace.Name;
  2978. visitTrace.AssignerId = pubTrace.HandlerId;
  2979. visitTrace.AssignerName = pubTrace.HandlerName;
  2980. visitTrace.AssignerOrgId = pubTrace.HandlerOrgId;
  2981. visitTrace.AssignerOrgName = pubTrace.HandlerOrgName;
  2982. visitTrace.AssignerOrgIsCenter = pubTrace.HandlerOrgIsCenter ?? false;
  2983. visitTrace.HandleTime = null;
  2984. visitTrace.AcceptorId = acceptor.UserId;
  2985. visitTrace.AcceptorName = acceptor.UserName;
  2986. visitTrace.AcceptorOrgId = acceptor.OrgId;
  2987. visitTrace.AcceptorOrgName = acceptor.OrgName;
  2988. visitTrace.StepExpiredTime = null;
  2989. await _workflowTraceRepository.AddAsync(visitTrace, cancellation);
  2990. return visitTrace;
  2991. }
  2992. private async Task<WorkflowTrace> CreateTrashEndTraceAsync(WorkflowTrace visitTrace, CancellationToken cancellation)
  2993. {
  2994. var now = DateTime.Now;
  2995. var TrashEndTrace = _mapper.Map<WorkflowTrace>(visitTrace);
  2996. TrashEndTrace.StepId = null;
  2997. TrashEndTrace.OrderPublishId = null;
  2998. TrashEndTrace.OrderVisitId = null;
  2999. TrashEndTrace.TraceStyle = ETraceStyle.TrashEnd;
  3000. TrashEndTrace.Name = "结束";
  3001. TrashEndTrace.Status = EWorkflowStepStatus.Handled;
  3002. TrashEndTrace.Code = "trashend";
  3003. TrashEndTrace.CreationTime = visitTrace.HandleTime ?? now;
  3004. TrashEndTrace.PrevStepId = visitTrace.Id;
  3005. TrashEndTrace.PrevStepCode = visitTrace.Code;
  3006. TrashEndTrace.PrevStepName = visitTrace.Name;
  3007. TrashEndTrace.AssignerId = visitTrace.HandlerId;
  3008. TrashEndTrace.AssignerName = visitTrace.HandlerName;
  3009. TrashEndTrace.AssignerOrgId = visitTrace.HandlerOrgId;
  3010. TrashEndTrace.AssignerOrgName = visitTrace.HandlerOrgName;
  3011. TrashEndTrace.AssignerOrgIsCenter = visitTrace.HandlerOrgIsCenter ?? false;
  3012. TrashEndTrace.AcceptorId = visitTrace.HandlerId;
  3013. TrashEndTrace.AcceptorName = visitTrace.HandlerName;
  3014. TrashEndTrace.AcceptorOrgId = visitTrace.HandlerOrgId;
  3015. TrashEndTrace.AcceptorOrgName = visitTrace.HandlerOrgName;
  3016. TrashEndTrace.AcceptTime = visitTrace.HandleTime ?? now;
  3017. TrashEndTrace.HandlerId = visitTrace.HandlerId;
  3018. TrashEndTrace.HandlerName = visitTrace.HandlerName;
  3019. TrashEndTrace.HandlerOrgId = visitTrace.HandlerOrgId;
  3020. TrashEndTrace.HandlerOrgName = visitTrace.HandlerOrgName;
  3021. TrashEndTrace.HandleTime = visitTrace.HandleTime ?? now;
  3022. TrashEndTrace.StepExpiredTime = null;
  3023. await _workflowTraceRepository.AddAsync(TrashEndTrace, cancellation);
  3024. return TrashEndTrace;
  3025. }
  3026. /// <summary>
  3027. /// 查询未完成节点
  3028. /// </summary>
  3029. public WorkflowStep GetUnHandleStep(List<WorkflowStep> steps, string orgId, string userId, string[] roleIds)
  3030. {
  3031. //var step = GetStep(steps, orgCode, userId, d => d != EWorkflowStepStatus.Handled);
  3032. var step = steps.FirstOrDefault(d => d.IsCanHandle(userId, orgId, roleIds));
  3033. if (step == null)
  3034. throw new UserFriendlyException(
  3035. $"未找到对应节点, workflowId: {steps.FirstOrDefault()?.WorkflowId} orgCode:{orgId}, userId: {userId}, roleIds: {string.Join(',', roleIds)}",
  3036. "未找到对应节点");
  3037. return step;
  3038. }
  3039. /// <summary>
  3040. /// 检查当前办理节点是否为开始节点
  3041. /// </summary>
  3042. /// <param name="workflowId"></param>
  3043. /// <param name="userId">当前办理人Id</param>
  3044. /// <param name="orgId">当前办理人orgId</param>
  3045. /// <param name="cancellationToken"></param>
  3046. /// <returns></returns>
  3047. public async Task<bool> CheckCurrentIsStartStepAsync(string workflowId, string userId, string orgId,
  3048. CancellationToken cancellationToken)
  3049. {
  3050. var workflow = await GetWorkflowAsync(workflowId, withSteps: true, cancellationToken: cancellationToken);
  3051. var currentStep = GetStep(workflow.Steps, orgId, userId, d => d != EWorkflowStepStatus.Handled);
  3052. if (currentStep is null) return false;
  3053. return workflow.Steps.Count == 1 && currentStep.StepType is EStepType.Start && currentStep.IsOrigin;
  3054. }
  3055. /// <summary>
  3056. /// 检查动态节点是否该终止
  3057. /// </summary>
  3058. public bool DynamicShouldTerminal(StepDefine currentStepDefine, int currentOrgLevel)
  3059. {
  3060. if (currentStepDefine.InstanceMode is not EInstanceMode.Dynamic)
  3061. throw new UserFriendlyException("非动态节点");
  3062. switch (currentStepDefine.InstancePolicy)
  3063. {
  3064. case EDynamicPolicy.OrgUpCenterTop:
  3065. case EDynamicPolicy.OrgUp:
  3066. case EDynamicPolicy.ArriveCenter:
  3067. case EDynamicPolicy.ArriveOneOrg:
  3068. case EDynamicPolicy.OrgUpHandleCenterTop:
  3069. case EDynamicPolicy.OrgUpHandle:
  3070. if (!int.TryParse(currentStepDefine.TerminalDynamicMark, out var tMark))
  3071. throw new UserFriendlyException(
  3072. $"TerminalDynamicMark parse to int failed, tMark: {currentStepDefine.TerminalDynamicMark}");
  3073. return currentOrgLevel <= tMark;
  3074. case EDynamicPolicy.OrgUpLeadCenterTop:
  3075. case EDynamicPolicy.OrgUpLead:
  3076. if (!int.TryParse(currentStepDefine.TerminalDynamicMark, out var tMark2))
  3077. throw new UserFriendlyException(
  3078. $"TerminalDynamicMark parse to int failed, tMark: {currentStepDefine.TerminalDynamicMark}");
  3079. var leadRoleCode = _systemSettingCacheManager.GetSetting(SettingConstants.RoleLingDao)?.SettingValue[0];
  3080. var isLead = _sessionContextProvider.SessionContext.Roles.Any(x => x == leadRoleCode);
  3081. return (currentOrgLevel <= tMark2) && (isLead || _sessionContextProvider.SessionContext.OrgIsCenter);
  3082. case EDynamicPolicy.OrgDownCenterTop:
  3083. case EDynamicPolicy.OrgDown:
  3084. if (!int.TryParse(currentStepDefine.TerminalDynamicMark, out var tMark1))
  3085. throw new UserFriendlyException(
  3086. $"TerminalDynamicMark parse to int failed, tMark: {currentStepDefine.TerminalDynamicMark}");
  3087. return currentOrgLevel >= tMark1;
  3088. default:
  3089. throw new ArgumentOutOfRangeException();
  3090. }
  3091. }
  3092. /// <summary>
  3093. /// 终止会签
  3094. /// </summary>
  3095. /// <param name="countersignId"></param>
  3096. /// <param name="cancellationToken"></param>
  3097. /// <returns></returns>
  3098. public async Task<Workflow> TerminalCountersignAsync(string countersignId, DateTime expireTime, CancellationToken cancellationToken)
  3099. {
  3100. var countersign = await _workflowCountersignRepository.GetAsync(countersignId, cancellationToken);
  3101. if (countersign is null)
  3102. throw new UserFriendlyException("无效会签编号");
  3103. //1. 检查会签是否已结束 t: return 2.检查是否有嵌套会签 t: 一起结束 3.结束会签 4.trace 5.检查workflow会签状态,如果会签全结束需更新状态 6.cp会签发起节点变为待办节点
  3104. if (countersign.IsCompleted())
  3105. throw new UserFriendlyException("该会签已结束");
  3106. return await TerminalCountersignAsync(countersign, expireTime, cancellationToken);
  3107. }
  3108. /// <summary>
  3109. /// 终止会签
  3110. /// </summary>
  3111. public async Task<Workflow> TerminalCountersignAsync(WorkflowCountersign countersign, DateTime expireTime,
  3112. CancellationToken cancellationToken)
  3113. {
  3114. // var countersign = await _workflowCountersignRepository.GetAsync(countersignId, cancellationToken);
  3115. // if (countersign is null)
  3116. // throw new UserFriendlyException("无效会签编号");
  3117. //1. 检查会签是否已结束 t: return 2.检查是否有嵌套会签 t: 一起结束 3.结束会签 4.trace 5.检查workflow会签状态,如果会签全结束需更新状态 6.cp会签发起节点变为待办节点
  3118. if (countersign.IsCompleted())
  3119. throw new UserFriendlyException("该会签已结束");
  3120. var workflow = await GetWorkflowAsync(countersign.WorkflowId, withSteps: true, withTraces: true,
  3121. withCountersigns: true, cancellationToken: cancellationToken);
  3122. if (!workflow.IsInCountersign)
  3123. throw new UserFriendlyException("该流程未处于会签中");
  3124. countersign = workflow.Countersigns.First(d => d.Id == countersign.Id);
  3125. var startCountersignStep = workflow.Steps.Find(d => d.StartCountersignId == countersign.Id);
  3126. if (startCountersignStep is null)
  3127. throw new UserFriendlyException("未查询到发起会签节点");
  3128. if (startCountersignStep.IsStartedCountersignEnd)
  3129. throw new UserFriendlyException("该会签已汇总");
  3130. var updateCountersigns = new List<WorkflowCountersign>();
  3131. EndCountersignWithCascade(countersign, workflow.Countersigns, startCountersignStep.BusinessType, ref updateCountersigns);
  3132. if (updateCountersigns.Any())
  3133. {
  3134. var updateSteps = new List<WorkflowStep>();
  3135. var updateTraces = new List<WorkflowTrace>();
  3136. HandleStepsByTerminalCs(startCountersignStep, workflow.Steps, workflow.Traces, ref updateSteps,
  3137. ref updateTraces);
  3138. if (updateSteps.Any())
  3139. await _workflowStepRepository.RemoveRangeAsync(updateSteps, cancellationToken);
  3140. //await _workflowStepRepository.RemoveNav(updateSteps)
  3141. // .Include(d => d.StepHandlers)
  3142. // .ExecuteCommandAsync();
  3143. if (updateTraces.Any())
  3144. await _workflowTraceRepository.UpdateRangeAsync(updateTraces, cancellationToken);
  3145. await _workflowCountersignRepository.UpdateRangeAsync(updateCountersigns, cancellationToken);
  3146. //cp会签发起节点变为待办节点
  3147. //1. create terminal trace 2. 撤回至startStep
  3148. var stepToDuplicate = startCountersignStep.IsCountersignEndStep
  3149. ? GetCsLoopStartStep(workflow.Steps, startCountersignStep)
  3150. : startCountersignStep;
  3151. var stepAssignInfo = GetStepAssignInfo(new ReverseFlowStepAssignInfo
  3152. {
  3153. ReverseFlowStepCreationPolicy = EReverseFlowStepCreationPolicy.OriginStep
  3154. }, stepToDuplicate);
  3155. var newStep = await DuplicateStepWithTraceAsync(workflow, stepToDuplicate, EWorkflowTraceType.Normal, stepAssignInfo, expireTime,
  3156. cancellationToken);
  3157. //当topcsStep结束cs时,实际办理节点应该更新为newStep
  3158. if (startCountersignStep.Id == workflow.TopCountersignStepId)
  3159. {
  3160. workflow.UpdateActualStepWhenAssign(newStep,
  3161. new FlowStepHandler
  3162. {
  3163. UserId = startCountersignStep.HandlerId,
  3164. Username = startCountersignStep.HandlerName,
  3165. OrgId = startCountersignStep.HandlerOrgId,
  3166. OrgName = startCountersignStep.HandlerOrgName,
  3167. RoleId = startCountersignStep.RoleId,
  3168. RoleName = startCountersignStep.RoleName
  3169. });
  3170. workflow.UpdateCurrentStepWhenAssign(newStep,
  3171. new FlowStepHandler
  3172. {
  3173. UserId = startCountersignStep.HandlerId,
  3174. Username = startCountersignStep.HandlerName,
  3175. OrgId = startCountersignStep.HandlerOrgId,
  3176. OrgName = startCountersignStep.HandlerOrgName,
  3177. RoleId = startCountersignStep.RoleId,
  3178. RoleName = startCountersignStep.RoleName
  3179. });
  3180. }
  3181. // //csEndStep又开启了cs,在结束会签时,如果该节点是topcs的end节点, workflow.topcsStep应该更新为前一cs开启stepId
  3182. // if (startCountersignStep.IsTopCountersignEndStep(workflow.TopCountersignStepId))
  3183. // workflow.TopCountersignStepId = startCountersignStep.CountersignStartStepId;
  3184. if (workflow.CheckIfCountersignOver())
  3185. workflow.EndCountersign();
  3186. await _workflowRepository.UpdateAsync(workflow, cancellationToken);
  3187. }
  3188. return workflow;
  3189. }
  3190. private void HandleStepsByTerminalCs(WorkflowStep step, List<WorkflowStep> steps, List<WorkflowTrace> traces,
  3191. ref List<WorkflowStep> updateSteps, ref List<WorkflowTrace> updateTraces)
  3192. {
  3193. if (step.IsStartCountersign)
  3194. {
  3195. var countersignSteps = steps.Where(d => d.CountersignId == step.StartCountersignId).ToList();
  3196. if (countersignSteps.Any())
  3197. {
  3198. foreach (var countersignStep in countersignSteps)
  3199. {
  3200. HandleStepsByTerminalCs(countersignStep, steps, traces, ref updateSteps, ref updateTraces);
  3201. }
  3202. }
  3203. }
  3204. EndStepByTerminalCs(step, traces, ref updateSteps, ref updateTraces);
  3205. }
  3206. private void EndStepByTerminalCs(WorkflowStep step, List<WorkflowTrace> traces,
  3207. ref List<WorkflowStep> updateSteps, ref List<WorkflowTrace> updateTraces)
  3208. {
  3209. var isHandled = step.Status is EWorkflowStepStatus.Handled;
  3210. var opinion = $"会签未办理完成,由 {_sessionContextProvider.SessionContext.OrgName} 的 {_sessionContextProvider.SessionContext.UserName} 终止办理";
  3211. if (step.IsStartCountersign)
  3212. step.CountersignEnd();
  3213. if (step.Status is not EWorkflowStepStatus.Handled)
  3214. {
  3215. step.Handle(_sessionContextProvider.SessionContext.RequiredUserId, _sessionContextProvider.SessionContext.UserName,
  3216. _sessionContextProvider.SessionContext.RequiredOrgId, _sessionContextProvider.SessionContext.OrgName,
  3217. _sessionContextProvider.SessionContext.OrgAreaCode, _sessionContextProvider.SessionContext.OrgAreaName,
  3218. _sessionContextProvider.SessionContext.OrgIsCenter, EHandleMode.Normal, opinion);
  3219. }
  3220. updateSteps.Add(step);
  3221. if (isHandled) return;
  3222. var trace = traces.FirstOrDefault(d => d.StepId == step.Id);
  3223. if (trace != null)
  3224. {
  3225. _mapper.Map(step, trace);
  3226. updateTraces.Add(trace);
  3227. }
  3228. }
  3229. /// <summary>
  3230. /// 结束会签(包含子项)
  3231. /// </summary>
  3232. /// <param name="countersign"></param>
  3233. /// <param name="updateCountersigns"></param>
  3234. private void EndCountersignWithCascade(WorkflowCountersign countersign, List<WorkflowCountersign> countersigns,
  3235. EBusinessType businessType, ref List<WorkflowCountersign> updateCountersigns)
  3236. {
  3237. if (countersign is null) return;
  3238. var childCountersigns = countersigns.Where(d => d.ParentId == countersign.Id).ToList();
  3239. if (childCountersigns.Any())
  3240. {
  3241. foreach (var childCountersign in childCountersigns)
  3242. {
  3243. EndCountersignWithCascade(childCountersign, countersigns, businessType, ref updateCountersigns);
  3244. }
  3245. }
  3246. EndCountersign(countersign, countersigns, businessType, ref updateCountersigns);
  3247. }
  3248. private void EndCountersign(WorkflowCountersign countersign, List<WorkflowCountersign> countersigns,
  3249. EBusinessType businessType, ref List<WorkflowCountersign> updateCountersigns)
  3250. {
  3251. //todo 1. trace? 先确定展现形式 2. end cs
  3252. countersign.End(null, null, businessType,
  3253. _sessionContextProvider.SessionContext.RequiredUserId, _sessionContextProvider.SessionContext.UserName,
  3254. _sessionContextProvider.SessionContext.RequiredOrgId, _sessionContextProvider.SessionContext.OrgName,
  3255. _sessionContextProvider.SessionContext.OrgAreaCode, _sessionContextProvider.SessionContext.OrgAreaName);
  3256. /*
  3257. * //结束step会签信息
  3258. countersignStartStep.CountersignEnd();
  3259. updateSteps.Add(countersignStartStep);
  3260. //结束会签
  3261. currentCountersign.End(currentStep.Id, currentStep.Code, currentStep.BusinessType,
  3262. _sessionContextProvider.SessionContext.RequiredUserId, _sessionContextProvider.SessionContext.UserName,
  3263. _sessionContextProvider.SessionContext.RequiredOrgId, _sessionContextProvider.SessionContext.OrgName,
  3264. _sessionContextProvider.SessionContext.OrgAreaCode, _sessionContextProvider.SessionContext.OrgAreaName);
  3265. await _workflowCountersignRepository.UpdateAsync(currentCountersign, cancellationToken);
  3266. */
  3267. updateCountersigns.Add(countersign);
  3268. }
  3269. private WorkflowStep? GetStep(List<WorkflowStep> steps, string orgCode, string userId,
  3270. Func<EWorkflowStepStatus, bool> predicate) =>
  3271. steps.FirstOrDefault(d =>
  3272. predicate(d.Status) && d.Handlers.Any(x => x.Key == orgCode || x.Key == userId));
  3273. private WorkflowStep CreateStep(
  3274. Workflow workflow,
  3275. StepDefine stepDefine,
  3276. WorkflowStep prevStep,
  3277. EFlowAssignType? flowAssignType,
  3278. FlowStepHandler handler,
  3279. UserInfo assigner,
  3280. string nextStepCode,
  3281. string? countersignId,
  3282. EWorkflowStepStatus stepStatus,
  3283. ECountersignPosition countersignPosition,
  3284. DateTime? expiredTime,
  3285. string stepName,
  3286. bool isOrigin,
  3287. bool isMainHandler = false,
  3288. EHandlerType? handlerType = null, //动态节点依据动态策略判断
  3289. EBusinessType? businessType = null,
  3290. EFlowDirection? flowDirection = null,
  3291. string? opinion = null
  3292. )
  3293. {
  3294. //if (!handlers.Any())
  3295. // throw new UserFriendlyException($"非法参数, handlers为空, method: {nameof(CreateStep)}");
  3296. var step = _mapper.Map<WorkflowStep>(stepDefine);
  3297. _mapper.Map(workflow, step);
  3298. step.FlowAssignType = flowAssignType;
  3299. step.Handlers = new List<Kv> { new(handler.Key, handler.Value) };
  3300. //step.StepHandlers = stepHandlers;
  3301. step.NextStepCode = step.StepType is EStepType.End ? string.Empty : nextStepCode;
  3302. step.IsMain = isMainHandler;
  3303. step.PrevStepId = prevStep.Id;
  3304. step.PrevStepCode = prevStep.Code;
  3305. step.PrevStepName = prevStep.Name;
  3306. step.CountersignId = countersignId;
  3307. step.Status = stepStatus;
  3308. step.CountersignPosition = countersignPosition;
  3309. step.FlowDirection = flowDirection;
  3310. if (expiredTime.HasValue)
  3311. step.StepExpiredTime = expiredTime;
  3312. //step.TimeLimit = GetTimeLimit("");
  3313. step.IsOrigin = isOrigin;
  3314. if (!string.IsNullOrEmpty(stepName))
  3315. step.Name = stepName;
  3316. //新增需求: 部门汇总节点由部门办理//todo 待确认中心由部门处理还是由之前办理人办理 待重构
  3317. if (step.StepType == EStepType.Summary && step.BusinessType == EBusinessType.Department)
  3318. step.FlowAssignType = EFlowAssignType.Org;
  3319. step.Assign(handler.UserId, handler.Username,
  3320. handler.OrgId, handler.OrgName,
  3321. handler.RoleId, handler.RoleName);
  3322. if (handlerType.HasValue)
  3323. step.HandlerType = handlerType.Value;
  3324. if (businessType.HasValue)
  3325. step.BusinessType = businessType.Value;
  3326. if (!string.IsNullOrEmpty(opinion))
  3327. step.Opinion = opinion;
  3328. step.AssignerId = assigner.UserId;
  3329. step.AssignerName = assigner.UserName;
  3330. step.AssignerOrgId = assigner.OrgId;
  3331. step.AssignerOrgName = assigner.OrgName;
  3332. step.AssignerOrgIsCenter = assigner.OrgIsCenter;
  3333. return step;
  3334. }
  3335. public async Task<FlowAssignInfo> GetNextStepFlowAssignInfoByDefineAsync(StepDefine nextStepDefine,
  3336. EHandlerType handlerType, bool isStartCountersign, List<Kv> handlers, CancellationToken cancellationToken)
  3337. {
  3338. switch (handlerType)
  3339. {
  3340. case EHandlerType.Role:
  3341. if (!handlers.Any())
  3342. {
  3343. //var roles = await _roleRepository.Queryable()
  3344. // .Includes(d => d.Accounts, x => x.User)
  3345. // .Where(d => nextStepDefine.HandlerTypeItems.Select(x => x.Key).Contains(d.Name))
  3346. // .ToListAsync(cancellationToken);
  3347. //handlers = roles.SelectMany(d => d.Accounts).Distinct()
  3348. // .Select(d => new Kv(d.Id, d.User.Name))
  3349. // .ToList();
  3350. handlers = nextStepDefine.HandlerTypeItems;
  3351. return FlowAssignInfo.Create(EFlowAssignType.Role, handlers, isStartCountersign);
  3352. }
  3353. return FlowAssignInfo.Create(EFlowAssignType.User, handlers, isStartCountersign);
  3354. case EHandlerType.OrgLevel:
  3355. case EHandlerType.OrgType:
  3356. case EHandlerType.AssignedOrg:
  3357. return FlowAssignInfo.Create(EFlowAssignType.Org, handlers, isStartCountersign);
  3358. case EHandlerType.AssignedUser:
  3359. return FlowAssignInfo.Create(EFlowAssignType.User, handlers, isStartCountersign);
  3360. //case EHandlerType.AssignedOrgOrRole:
  3361. // return FlowAssignInfo.Create(EFlowAssignType.OrgAndRole, handlers, isStartCountersign);
  3362. default:
  3363. throw new ArgumentOutOfRangeException();
  3364. }
  3365. }
  3366. //new
  3367. /// <summary>
  3368. /// 查询流程业务模块
  3369. /// </summary>
  3370. /// <param name="code"></param>
  3371. /// <param name="cancellationToken"></param>
  3372. /// <returns></returns>
  3373. /// <exception cref="UserFriendlyException"></exception>
  3374. private async Task<WorkflowModule> GetWorkflowModuleAsync(string code, CancellationToken cancellationToken)
  3375. {
  3376. var wfModule = await _wfModuleCacheManager.GetWorkflowModuleAsync(code, cancellationToken);
  3377. if (wfModule == null)
  3378. throw UserFriendlyException.SameMessage("无效流程模块编码");
  3379. if (wfModule.Definition is null)
  3380. throw new UserFriendlyException($"{code} 未配置流程模板", "未配置流程模板");
  3381. return wfModule;
  3382. }
  3383. /// <summary>
  3384. /// 查询下一节点办理对象类型(user or org)及实际办理对象
  3385. /// </summary>
  3386. private async Task<FlowAssignInfo> GetNextStepFlowAssignInfoAsync(Workflow workflow, WorkflowStep currentStep,
  3387. BasicWorkflowDto dto, StepDefine nextStepDefine, bool isNextDynamic, CancellationToken cancellationToken)
  3388. {
  3389. if (nextStepDefine.StepType is EStepType.End) return new();
  3390. var isStartCountersign = dto.IsStartCountersign;
  3391. var handlers = dto.NextHandlers.Select(d => new Kv(d.Key, d.Value)).ToList();
  3392. if (isStartCountersign)
  3393. {
  3394. var assignType = FlowAssignInfo.GetAssignType(dto.HandlerType, dto.NextHandlers.Any());
  3395. //按会签策略判断,目前所有策略为org
  3396. return FlowAssignInfo.Create(assignType, handlers, isStartCountersign);
  3397. }
  3398. //if (currentStep.IsInCountersign() && !currentStep.IsTopCountersignEndStep(workflow.TopCountersignStepId))
  3399. // return FlowAssignInfo.Create(EFlowAssignType.Org, handlers, isStartCountersign);
  3400. if (currentStep.IsInCountersign())
  3401. {
  3402. if (currentStep.IsCountersignEndStep)
  3403. {
  3404. //汇总节点(非顶级)
  3405. if (!currentStep.IsTopCountersignEndStep(workflow.TopCountersignStepId))
  3406. {
  3407. if (dto.BackToCountersignEnd)
  3408. {
  3409. var csStartStep = GetCsLoopStartStep(workflow.Steps, currentStep);
  3410. var prevStep = workflow.Steps.FirstOrDefault(d => d.Id == csStartStep.PrevStepId);
  3411. if (prevStep is null)
  3412. throw new UserFriendlyException("未查询到目标节点的前一节点");
  3413. return FlowAssignInfo.Create(prevStep.FlowAssignType.Value, prevStep.Handlers, isStartCountersign);
  3414. }
  3415. }
  3416. }
  3417. else
  3418. {
  3419. if (dto.BackToCountersignEnd)
  3420. {
  3421. var prevStep = workflow.Steps.FirstOrDefault(d => d.Id == currentStep.PrevStepId);
  3422. if (prevStep is null)
  3423. throw new UserFriendlyException($"未查询到当前节点的上级节点");
  3424. return FlowAssignInfo.Create(prevStep.FlowAssignType.Value, prevStep.Handlers, isStartCountersign);
  3425. }
  3426. else
  3427. {
  3428. var assignType = FlowAssignInfo.GetAssignType(dto.HandlerType, dto.NextHandlers.Any());
  3429. //按会签策略判断,目前所有策略为org
  3430. return FlowAssignInfo.Create(assignType, handlers, isStartCountersign);
  3431. }
  3432. }
  3433. }
  3434. if (isNextDynamic)
  3435. {
  3436. switch (currentStep.InstancePolicy)
  3437. {
  3438. case EDynamicPolicy.OrgUpCenterTop:
  3439. case EDynamicPolicy.OrgUp:
  3440. case EDynamicPolicy.OrgDownCenterTop:
  3441. case EDynamicPolicy.OrgDown:
  3442. case EDynamicPolicy.ArriveCenter:
  3443. case EDynamicPolicy.ArriveOneOrg:
  3444. return FlowAssignInfo.Create(EFlowAssignType.Org, handlers, isStartCountersign);
  3445. case EDynamicPolicy.OrgUpHandleCenterTop:
  3446. case EDynamicPolicy.OrgUpHandle:
  3447. case EDynamicPolicy.OrgUpLeadCenterTop:
  3448. case EDynamicPolicy.OrgUpLead:
  3449. return FlowAssignInfo.Create(EFlowAssignType.OrgAndRole, handlers, isStartCountersign);
  3450. default:
  3451. throw new ArgumentOutOfRangeException();
  3452. }
  3453. }
  3454. return await GetNextStepFlowAssignInfoByDefineAsync(nextStepDefine, dto.HandlerType, isStartCountersign, handlers,
  3455. cancellationToken);
  3456. }
  3457. private EFlowAssignType SetNextStepAssignInfo(Workflow workflow, WorkflowStep currentStep,
  3458. BasicWorkflowDto dto, StepDefine nextStepDefine, bool isNextDynamic)
  3459. {
  3460. if (nextStepDefine.StepType is EStepType.End) return EFlowAssignType.User;
  3461. if (dto.IsStartCountersign)
  3462. return GetFlowStepAssignType(dto.HandlerType, dto.NextHandlers.Any());
  3463. if (currentStep.IsInCountersign())
  3464. {
  3465. if (currentStep.IsCountersignEndStep)
  3466. {
  3467. //汇总节点(非顶级)
  3468. if (!currentStep.IsTopCountersignEndStep(workflow.TopCountersignStepId))
  3469. {
  3470. if (dto.BackToCountersignEnd)
  3471. {
  3472. var csStartStep = GetCsLoopStartStep(workflow.Steps, currentStep);
  3473. var prevStep = workflow.Steps.FirstOrDefault(d => d.Id == csStartStep.PrevStepId);
  3474. if (prevStep is null)
  3475. throw new UserFriendlyException("未查询到目标节点的前一节点");
  3476. return prevStep.FlowAssignType ?? EFlowAssignType.User;
  3477. }
  3478. }
  3479. }
  3480. else
  3481. {
  3482. if (dto.BackToCountersignEnd)
  3483. {
  3484. var prevStep = workflow.Steps.FirstOrDefault(d => d.Id == currentStep.PrevStepId);
  3485. if (prevStep is null)
  3486. throw new UserFriendlyException($"未查询到当前节点的上级节点");
  3487. return prevStep.FlowAssignType ?? EFlowAssignType.User;
  3488. }
  3489. else
  3490. {
  3491. return GetFlowStepAssignType(dto.HandlerType, dto.NextHandlers.Any());
  3492. }
  3493. }
  3494. }
  3495. if (isNextDynamic)
  3496. {
  3497. switch (currentStep.InstancePolicy)
  3498. {
  3499. case EDynamicPolicy.OrgUpCenterTop:
  3500. case EDynamicPolicy.OrgUp:
  3501. case EDynamicPolicy.OrgDownCenterTop:
  3502. case EDynamicPolicy.OrgDown:
  3503. case EDynamicPolicy.ArriveCenter:
  3504. case EDynamicPolicy.ArriveOneOrg:
  3505. return EFlowAssignType.Org;
  3506. case EDynamicPolicy.OrgUpHandleCenterTop:
  3507. case EDynamicPolicy.OrgUpHandle:
  3508. case EDynamicPolicy.OrgUpLeadCenterTop:
  3509. case EDynamicPolicy.OrgUpLead:
  3510. return EFlowAssignType.OrgAndRole;
  3511. default:
  3512. throw new ArgumentOutOfRangeException();
  3513. }
  3514. }
  3515. return GetFlowStepAssignType(dto.HandlerType, dto.NextHandlers.Any());
  3516. }
  3517. public EFlowAssignType GetFlowStepAssignType(EHandlerType handlerType, bool hasNextHandlers)
  3518. {
  3519. return handlerType switch
  3520. {
  3521. EHandlerType.Role => hasNextHandlers ? EFlowAssignType.User : EFlowAssignType.Role,
  3522. EHandlerType.OrgLevel => EFlowAssignType.Org,
  3523. EHandlerType.OrgType => EFlowAssignType.Org,
  3524. EHandlerType.AssignedUser => EFlowAssignType.User,
  3525. EHandlerType.AssignedOrg => EFlowAssignType.Org,
  3526. _ => throw new ArgumentOutOfRangeException(nameof(handlerType), handlerType, null)
  3527. };
  3528. }
  3529. #endregion
  3530. }
  3531. }