WorkflowStep.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  1. using Hotline.File;
  2. using Hotline.Share.Dtos;
  3. using Hotline.Share.Dtos.FlowEngine.Definition;
  4. using Hotline.Share.Dtos.FlowEngine.Workflow;
  5. using Hotline.Share.Enums.FlowEngine;
  6. using SqlSugar;
  7. using XF.Domain.Exceptions;
  8. namespace Hotline.FlowEngine.Workflows;
  9. public class WorkflowStep : StepBasicEntity
  10. {
  11. /// <summary>
  12. /// 配置下一步节点 & 谁被选中
  13. /// </summary>
  14. [SugarColumn(ColumnDataType = "json", IsJson = true)]
  15. public List<StepSimple> NextSteps { get; set; } = new();
  16. /// <summary>
  17. /// 前一级节点Id,会签汇总节点无此字段(因可能有多个上级来源)
  18. /// </summary>
  19. public string? PrevStepId { get; set; }
  20. public string? PrevStepCode { get; set; }
  21. /// <summary>
  22. /// 主办
  23. /// </summary>
  24. public bool IsMain { get; set; }
  25. /// <summary>
  26. /// 原生节点(区别动态生成)
  27. /// </summary>
  28. public bool IsOrigin { get; set; }
  29. #region 会签
  30. /// <summary>
  31. /// 会签id(或外层会签的id)
  32. /// </summary>
  33. public string? CountersignId { get; set; }
  34. /// <summary>
  35. /// 节点处于会签流程中的位置(区别直接办理会签和会签内非会签节点)
  36. /// outer属于特殊会签
  37. /// 最顶层的发起会签节点为none
  38. /// </summary>
  39. public ECountersignPosition CountersignPosition { get; set; }
  40. /// <summary>
  41. /// 会签直属办理节点
  42. /// </summary>
  43. [SugarColumn(ColumnDataType = "json", IsJson = true)]
  44. public List<CountersignStep>? CountersignSteps { get; set; } = new();
  45. /// <summary>
  46. /// 发起的会签是否汇总
  47. /// </summary>
  48. public bool IsStartedCountersignEnd { get; set; }
  49. #region 发起会签节点特有
  50. /// <summary>
  51. /// 发起会签生成会签Id(不发起会签节点无此字段)
  52. /// </summary>
  53. public string? StartCountersignId { get; set; }
  54. #endregion
  55. #region 会签汇总节点特有
  56. /// <summary>
  57. /// 是否为会签汇总节点
  58. /// </summary>
  59. public bool IsCountersignEndStep { get; set; }
  60. /// <summary>
  61. /// 开启会签节点id(会签汇总节点对应会签发起节点id)
  62. /// </summary>
  63. public string? CountersignStartStepId { get; set; }
  64. #endregion
  65. #endregion
  66. [Navigate(NavigateType.ManyToOne, nameof(WorkflowId))]
  67. public Workflow Workflow { get; set; }
  68. [Navigate(NavigateType.OneToOne, nameof(Id),
  69. nameof(Workflows.WorkflowTrace.StepId))]
  70. public WorkflowTrace WorkflowTrace { get; set; }
  71. [Navigate(NavigateType.OneToMany, nameof(WorkflowStepHandler.WorkflowStepId))]
  72. public List<WorkflowStepHandler> StepHandlers { get; set; }
  73. #region Method
  74. /// <summary>
  75. /// 是否处于会签流程中(不包含顶层发起会签节点)
  76. /// </summary>
  77. public bool IsInCountersign() => CountersignPosition != ECountersignPosition.None;
  78. ///// <summary>
  79. ///// 是否发起过会签
  80. ///// </summary>
  81. //public bool HasStartedCountersign() => !string.IsNullOrEmpty(StartCountersignId);
  82. /// <summary>
  83. /// 接办
  84. /// </summary>
  85. public void Accept(string userId, string? userName, string orgId, string? orgName, string? orgAreaCode,
  86. string? orgAreaName)
  87. {
  88. AcceptorId = userId;
  89. AcceptorName = userName;
  90. AcceptorOrgId = orgId;
  91. AcceptorOrgName = orgName;
  92. AcceptorOrgAreaCode = orgAreaCode;
  93. AcceptorOrgAreaName = orgAreaName;
  94. AcceptTime = DateTime.Now;
  95. Status = EWorkflowStepStatus.WaitForHandle;
  96. }
  97. /// <summary>
  98. /// 节点办理完成
  99. /// </summary>
  100. public void Handle(
  101. string userId, string? userName,
  102. string orgId, string? orgName,
  103. string? orgAreaCode, string? orgAreaName,
  104. bool orgIsCenter, string opinion, string nextStepCode)
  105. {
  106. base.Handle(userId, userName, orgId, orgName, orgAreaCode, orgAreaName, orgIsCenter, opinion);
  107. Status = EWorkflowStepStatus.Handled;
  108. if (!IsInCountersign()
  109. && InstanceMode is EInstanceMode.Config
  110. && StepType is not EStepType.End)
  111. {
  112. var step = NextSteps.FirstOrDefault(d => d.Code == nextStepCode);
  113. if (step != null)
  114. step.Selected = true;
  115. }
  116. }
  117. /// <summary>
  118. /// 会签结束
  119. /// </summary>
  120. public void CountersignEnd() => IsStartedCountersignEnd = true;
  121. /// <summary>
  122. /// 开启会签
  123. /// </summary>
  124. public void StartCountersign(string startCountersignId)
  125. {
  126. IsStartCountersign = true;
  127. StartCountersignId = startCountersignId;
  128. IsStartedCountersignEnd = false;
  129. }
  130. //public void CreateCountersignSteps(List<WorkflowStep> nextSteps) =>
  131. // CountersignSteps = nextSteps
  132. // .Select(d => new CountersignStep { StepId = d.Id })
  133. // .ToList();
  134. /// <summary>
  135. /// step设置为可接办状态
  136. /// </summary>
  137. public void SetAssigned() => Status = EWorkflowStepStatus.WaitForAccept;
  138. /// <summary>
  139. /// 依据当前节点获取下一节点会签状态
  140. /// </summary>
  141. /// <returns></returns>
  142. public ECountersignPosition GetNextStepCountersignPosition() =>
  143. IsStartCountersign
  144. ? ECountersignPosition.Inner
  145. : IsInCountersign()
  146. ? ECountersignPosition.Outer
  147. : ECountersignPosition.None;
  148. public bool IsCenter() => BusinessType is EBusinessType.Center or EBusinessType.Send;
  149. public bool IsOrg() => BusinessType is EBusinessType.Department;
  150. /// <summary>
  151. /// 重置节点,只清除办理痕迹(退回场景/依据会签开始节点复制会签汇总节点,将上一级节点重置等待重新办理)
  152. /// </summary>
  153. public void Reset()
  154. {
  155. HandlerId = null;
  156. HandlerName = null;
  157. HandlerOrgId = null;
  158. HandlerOrgName = null;
  159. HandlerOrgAreaCode = null;
  160. HandlerOrgAreaName = null;
  161. HandleTime = null;
  162. AcceptorId = null;
  163. AcceptorName = null;
  164. AcceptorOrgId = null;
  165. AcceptorOrgName = null;
  166. AcceptorOrgAreaCode = null;
  167. AcceptorOrgAreaName = null;
  168. AcceptTime = null;
  169. IsStartCountersign = false;
  170. NextSteps.ForEach(d => d.Selected = false);
  171. }
  172. /// <summary>
  173. /// 重置办理参数
  174. /// </summary>
  175. public void ResetParameters()
  176. {
  177. NextHandlers = new();
  178. NextMainHandler = null;
  179. NextStepCode = null;
  180. BackToCountersignEnd = false;
  181. IsSms = false;
  182. Opinion = null;
  183. FileJson = new();
  184. StepExpiredTime = null;
  185. }
  186. ///// <summary>
  187. ///// 发起会签节点发起的会签是否全都办理完成
  188. ///// </summary>
  189. ///// <returns></returns>
  190. //public bool StartedCountersignHasAllHandled()
  191. //{
  192. // //if (!HasStartedCountersign())
  193. // // throw new UserFriendlyException("该节点未发起会签");
  194. // //outer属于特殊会签
  195. // return CountersignSteps.All(d => d.Completed);
  196. //}
  197. /// <summary>
  198. /// 动态实例化节点是否应该终止
  199. /// </summary>
  200. /// <returns></returns>
  201. public bool DynamicShouldTerminal() => !string.IsNullOrEmpty(NextStepCode) && TerminalDynamicMark == NextStepCode;
  202. /// <summary>
  203. /// 是否是顶级会签汇总节点
  204. /// </summary>
  205. public bool IsTopCountersignEndStep(string? topCountersignStepId)
  206. {
  207. if (string.IsNullOrEmpty(topCountersignStepId))
  208. throw new UserFriendlyException($"无效顶级会签节点编号,wfId: {WorkflowId}");
  209. return IsCountersignEndStep && CountersignStartStepId == topCountersignStepId;
  210. }
  211. public WorkflowStepHandler?
  212. FindActualHandler(ICollection<string> roles, string? userId = null, string? orgId = null) =>
  213. StepHandlers.FirstOrDefault(d => d.IsHandler(roles, userId, orgId));
  214. public WorkflowStepHandler? GetActualHandler() =>
  215. StepHandlers.FirstOrDefault(d => d.IsActualHandler);
  216. #endregion
  217. }
  218. /*
  219. 获取开始节点下一步待选节点:
  220. 是否属于多次实例化配置(dynamic动态实例化配置:结合办理人动态生成待办节点)
  221. t:无需检查是否到结束标识,所以按配置的重复实例化策略结合当前操作人生成待办节点,如:生成x级部门办理,下一接口再选具体部门
  222. f:开始节点配置的下一节点集合
  223. 获取开始节点所选下一步节点的具体参数:
  224. 是否属于多次实例化配置
  225. t:所选x级部门返回对应办理对象集合(具体部门)
  226. f:按模板配置返回办理对象集合
  227. 获取待办节点下一步待选节点:
  228. 是否属于多次实例化配置(无需检查definetion,直接找到当前待办节点获取参数(需要的参数冗余至待办节点上))
  229. t:检查是否到结束标识(检查当前办理节点的办理对象与结束标识(orglevel:int)是否一致)
  230. t:返回配置的下一节点(看作单次实例化,当前节点已办完)//definitionId,stepCodes,按配置走
  231. f:按配置的重复实例化策略结合当前操作人,生成如:{x:X级部门办理},{0:中心办理},最高级最多只能到中心,按动态策略走,动态策略枚举
  232. 是否处于会签中
  233. t:按会签策略的下一级 + 上一级(isCSEnd会签汇总节点标记),并对上一级选项打上标记,如:{x:X级部门办理},{0:中心办理},按动态策略走,动态策略枚举
  234. f:按照definition(按配置走)
  235. 返回当前节点的配置下一步
  236. 获取下一步的具体参数:
  237. 检查模式
  238. 按动态策略:依据当前操作人、动态类型枚举 查询对应待选部门
  239. 按配置策略:原有实现方案
  240. start:
  241. check配置是否允许当前用户发起
  242. next: (nextStepCode/x, userId[]/orgId[],dto.other)
  243. check该不该当前用户办(handlers,all不需要check)
  244. 办理当前节点
  245. 创建后面节点
  246. 是否动态实例化
  247. t:检查是否到结束标识(参数orgId==mark?)
  248. t:(参数时stepcode)按配置创建
  249. f:(参数是x级部门)按动态实例化策略创建
  250. 是否处于会签中
  251. t: 是否返回上一级汇总 (isCSEnd)
  252. t: 1.update上一级的会签完成情况[]
  253. 2.如果会签全完成,创建新的待办汇总节点(IsCountersignSummaryStep = true)?更新发起节点状态?thk
  254. f: 创建新的下一步节点
  255. */